简体   繁体   English

如何以编程方式将新子项添加到深度嵌套的对象中

[英]How to programatically add new children to a deeply nested object

Consider the following object, which is composed by an unknown number of deeply nested children.考虑以下对象,它由数量未知的深度嵌套子项组成。

 const state = {
  id: 1,
  children: [
    {
      id: 3,
      children: [
        {
          id: 4,
          children: []
        }
      ]
    },
    {
      id: 2,
      children: []
    }
  ]
}

How can I programatically push a new object to the children array of a node knowing only its id and the array of id s of its parents?我如何编程推向一个新的对象到children节点的数组只知道它的id和数组id的父母第? I thought using recursion but I couldn't find a solution that worked.我想使用递归,但我找不到有效的解决方案。 I am also using immutability-helper, so I have tried using Array.reduce() to return an object that looked like this:我也在使用 immutability-helper,所以我尝试使用Array.reduce()返回一个看起来像这样的对象:

    const newState = {
      children: {
        [idxOfNodeToChange]: {
          children: {$push: newChildren}
        }
      }
    }

so I could pass it to update() but there I am even more stuck since I would still have to traverse through the accumulator every time to go as deep as needed, and I'm not sure how to do that.所以我可以将它传递给update()但在那里我更加卡住了,因为我每次仍然需要遍历累加器才能根据需要深入,我不知道如何做到这一点。 Any ideas?有任何想法吗?

Extra info: I'm using a D3 library for React called VX and this structure is required to build a tree component, so I'm stuck on how to add new nodes programatically.额外信息:我正在使用一个名为 VX 的 React D3 库,构建树组件需要这种结构,所以我一直在思考如何以编程方式添加新节点。

Let's find the node with ID 3 in our state, searching one level deep.让我们在我们的状态中找到 ID 为 3 的节点,搜索一层深。 Take the children of the current node , and find the node with correct ID within those children:当前节点节点,并在这些子节点中找到具有正确 ID 的节点

id = 3
node3 = state.children.find(v => v == id)

In that node, find ID 4. Now we're searching in the children of node 3 :在该节点中,找到 ID 4。现在我们正在搜索节点 3的子节点

id = 4 // ┌ act on node3!
node4 = node3.children.find(v => v == id)

Fitting that to Array.reduce() , the accumulator is the current node .将其拟合到Array.reduce()累加器当前节点 It starts at the root node state , and then traverses the tree downwards: each time, we traverse the tree one level, using the next ID from a list of IDs.它从根节点state ,然后向下遍历树:每次,我们使用 ID 列表中的下一个 ID 遍历树一级。 We need the recursion to start at the root of the tree, so the initial value is state , our root node.我们需要递归从树的根开始,所以初始值是state ,我们的根节点。

If we take the above examples and reduce them:如果我们采用上面的例子并reduce它们:

[3, 4].reduce((acc, x) => acc.children.find(v => v === x), state)
// ids                    traverse one level               start at root node

Unrolling it, this is equivalent to:展开它,这相当于:

(state.children.find(v => v === 3)).children.find(v => v === 4)

The general form becomes:一般形式变为:

const recursiveTraversal = ids => 
  ids.reduce((acc, x) => acc.children.find(v => v === x), state)

Here you go.干得好。 You can use this recursive function to search by id and append data to the found node's children array.您可以使用此递归函数按 id 搜索并将数据附加到找到的节点的 children 数组。

function appendChildToNode(node, nodeId, data) {
  // If the node is empty, just return it as is.
  if (!node) {
    return node;
  }

  let children;

  if (node.id === nodeId) {
    // If the node has the id we're searching for, 
    // append the data to its children.
    children = [...node.children, data];
  } else {
    // Otherwise, apply the function recursively to each of its children 
    children = node.children.map(childNode => appendChildToNode(childNode, nodeId, data));
  }  

  return { ...node, children };
}

It is immutable and you may use it like this:它是不可变的,你可以这样使用它:

const newState1 = appendChildToNode(state, 4, { id: 5, children: [] });
const newState2 = appendChildToNode(state, 2, { id: 5, children: [] });

See it working in the example snippet below.在下面的示例片段中查看它的工作情况。

 const state = { id: 1, children: [{ id: 3, children: [{ id: 4, children: [] }] }, { id: 2, children: [] } ] }; const newState1 = appendChildToNode(state, 4, { id: 5, children: [] }); const newState2 = appendChildToNode(state, 2, { id: 5, children: [] }); console.log(state); // Orginal state should not be mutated. console.log(newState1); console.log(newState2); function appendChildToNode(node, nodeId, data) { // If the node is empty, just return it as is. if (!node) { return node; } let children; if (node.id === nodeId) { // If the node has the id we're searching for, // append the data to its children. children = [...node.children, data]; } else { // Otherwise, apply the function recursively to each of its children children = node.children.map(childNode => appendChildToNode(childNode, nodeId, data)); } return { ...node, children }; }


Update: The above function uses ES6 spread syntax to append items.更新:上述函数使用ES6 扩展语法来附加项目。 If you need to support older browsers w/o transpilation, you can use this updated version using Object.assign and Array#concat .如果你需要支持没有转译的旧浏览器,你可以使用这个更新版本使用Object.assignArray#concat

function appendChildToNode(node, nodeId, data) {
  if (!node) {
    return node;
  }

  var children;
  
  if (node.id === nodeId) {
    children = node.children.concat([data]);
  } else {
    children = node.children.map(childNode => appendChildToNode(childNode, nodeId, data));
  }
  
  return Object.assign({}, node, { children });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM