【问题标题】:Remove matched object from deeply nested array of objects从深度嵌套的对象数组中删除匹配的对象
【发布时间】:2019-03-09 23:20:49
【问题描述】:

我有一个带有孩子的数据树结构:

{  id: 1,
   name: "Dog",
   parent_id: null,
   children: [
         {
             id: 2,
             name: "Food",
             parent_id: 1,
             children: []
         },
         {
             id: 3,
             name: "Water",
             parent_id: 1,
             children: [
                 {
                    id: 4,
                    name: "Bowl",
                    parent_id: 3,
                    children: []
                 },
                 {
                    id: 5,
                    name: "Oxygen",
                    parent_id: 3,
                    children: []
                 },
                 {
                    id: 6,
                    name: "Hydrogen",
                    parent_id: 3,
                    children: []
                 }
             ]
         }
   ]
}

这表示一个 DOM 结构,用户可以通过单击 DOM 中的相应按钮从中选择要删除的项目。

我有一个已知的从 DOM 中删除的项目的文本标题设置为变量 clickedTitle。我无法找到允许我从深度嵌套树中删除正确对象数据的算法。

这是我的代码:

function askUserForDeleteConfirmation(e) {
    const okToDelete = confirm( 'Are you sure you want to delete the item and all of its sub items?' );
    if(!okToDelete) {
        return;
    }
    const tree = getTree(); // returns the above data structure
    const clickedTitle = getClickedTitle(e); // returns string title of clicked on item from DOM - for example "Dog" or "Bowl"
    const updatedTree = removeFromTree(tree, tree, clickedTitle);

    return updatedTree;
}

function removeFromTree(curNode, newTree, clickedTitle) {
    if(curNode.name === clickedTitle) {
        // this correctly finds the matched data item to delete but the next lines don't properly delete it... what to do?
        const index = curNode.children.findIndex(child => child.name === clickedTitle);
        newTree = curNode.children.slice(index, index + 1);
        // TODO - what to do here?
    }

    for(const node of curNode.children) {
        removeFromTree(node, newTree, clickedTitle);
    }

    return newTree;
}

我尝试使用来自Removing matched object from array of objects using javascript 的信息但没有成功。

【问题讨论】:

  • 您能否提供一个示例案例中函数应用的期望结果?
  • 同时检查 curNode.name === clickedTitlechild => child.name === clickedTitle 对我来说没有意义。
  • 没错,这就是为什么我要一个期望效果的例子
  • 期望的结果是从原始树中删除该项目。
  • ggorlen 的回答没有任何问题。 Victor 的回答似乎确实有效,但应用程序无法正常工作,导致出现奇怪的行为。

标签: javascript arrays object


【解决方案1】:

如果您不介意修改参数树in-place,这应该可以完成工作。请注意,如果您尝试删除根,它将返回 null

const tree = { id: 1, name: "Dog", parent_id: null, children: [ { id: 2, name: "Food", parent_id: 1, children: [] }, { id: 3, name: "Water", parent_id: 1, children: [ { id: 4, name: "Bowl", parent_id: 3, children: [] }, { id: 5, name: "Oxygen", parent_id: 3, children: [] }, { id: 6, name: "Hydrogen", parent_id: 3, children: [] } ] } ] };

const removeFromTree = (root, nameToDelete, parent, idx) => {
  if (root.name === nameToDelete) {
    if (parent) {
      parent.children.splice(idx, 1);
    }
    else return null;
  }
  
  for (const [i, e] of root.children.entries()) {
    removeFromTree(e, nameToDelete, root, i);
  }
  
  return tree;
};

console.log(removeFromTree(tree, "Oxygen"));

您当前的代码非常正确。然而:

newTree = curNode.children.slice(index, index + 1);

强调几个问题:我们需要操纵父级的children 数组来删除curNode 而不是curNode 自己的children 数组。我通过调用递归地传递父对象和子索引,省去了线性操作findIndex的麻烦。

另外,从index 切片到index + 1 只提取一个元素,不会修改curNode.children。如何使用newArray 或通过调用堆栈返回它并不明显。 splice 似乎是更适合手头任务的工具:就地提取一个元素。

注意这个函数会删除多个匹配nameToDelete的条目。

【讨论】:

    【解决方案2】:

    我喜欢@VictorNascimento 的回答,但通过应用map 然后filter,每个children 列表将被迭代两次。这是reduce 的替代方法以避免这种情况:

    function removeFromTree(node, name) {
      return node.name == name
        ? undefined
        : {
            ...node,
            children: node.children.reduce(
              (children, child) => children.concat(removeFromTree (child, name) || []), [])
          }
    }
    

    如果您想要一种就地删除项目的方法,正如@ggorlen 建议的那样,我建议您使用以下解决方案,这在我看来更简单:

    function removeFromTree(node, name) {
      if (node.name == name) {
        node = undefined
      } else {
        node.children.forEach((child, id) => {
          if (!removeFromTree(child, name)) node.children.splice(id, 1)
        })
      }
      return node
    }
    

    【讨论】:

      【解决方案3】:

      我构建的算法如下:

      function omitNodeWithName(tree, name) {
        if (tree.name === name) return undefined;
      
        const children = tree.children.map(child => omitNodeWithName(child, name))
          .filter(node => !!node);
      
        return {
          ...tree,
          children
        }  
      }
      

      您可以使用它来返回没有该项目的新树:

      noHydrogen = omitNodeWithName(tree, "Hydrogen")
      

      【讨论】:

        【解决方案4】:

        如果可以使用 Lodash+Deepdash,那么:

        let cleaned = _.filterDeep([tree],(item)=>item.name!='Hydrogen',{tree:true});
        

        这是Codepen

        【讨论】:

          【解决方案5】:

          我们将object-scan 用于许多数据处理任务。一旦你把头绕在它周围,它就会很强大。以下是您如何回答您的问题

          // const objectScan = require('object-scan');
          
          const prune = (name, input) => objectScan(['**[*]'], {
            rtn: 'bool',
            abort: true,
            filterFn: ({ value, parent, property }) => {
              if (value.name === name) {
                parent.splice(property, 1);
                return true;
              }
              return false;
            }
          })(input);
          
          const obj = { id: 1, name: 'Dog', parent_id: null, children: [{ id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [{ id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 5, name: 'Oxygen', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] }] }] };
          
          console.log(prune('Oxygen', obj)); // return true iff pruned
          // => true
          
          console.log(obj);
          // => { id: 1, name: 'Dog', parent_id: null, children: [ { id: 2, name: 'Food', parent_id: 1, children: [] }, { id: 3, name: 'Water', parent_id: 1, children: [ { id: 4, name: 'Bowl', parent_id: 3, children: [] }, { id: 6, name: 'Hydrogen', parent_id: 3, children: [] } ] } ] }
          .as-console-wrapper {max-height: 100% !important; top: 0}
          <script src="https://bundle.run/object-scan@13.8.0"></script>

          免责声明:我是object-scan的作者

          【讨论】:

            猜你喜欢
            • 2020-10-30
            • 1970-01-01
            • 2021-12-28
            • 1970-01-01
            • 2021-08-12
            • 2020-12-07
            • 2020-08-02
            • 1970-01-01
            • 2019-11-02
            相关资源
            最近更新 更多