在像数据结构这样的嵌套树中,如何通过父节点的 id 将子节点添加到父节点的子数组?

如何解决在像数据结构这样的嵌套树中,如何通过父节点的 id 将子节点添加到父节点的子数组?

我有一个 json 对象:

const data = {
  id: 'root',name: 'Parent',children: [
    {
      id: '1',name: 'Child - 1',},{
      id: '3',name: 'Child - 3',children: [
        {
          id: '4',name: 'Child - 4',],};

而且我想通过 id 将新的子数组动态添加到任何父元素。例如。我想找到 id='4' 的元素并添加子项:[{ id:'5',name: 'Child - 4' }]

解决方法

id 匹配时,您可以构建一个递归函数来向子数组添加数据。

const data = [{ id: 'root',name: 'Parent',children: [ { id: '1',name: 'Child - 1',},{ id: '3',name: 'Child - 3',children: [ { id: '4',name: 'Child - 4',],}];

const addDataToId=(arr,id,children)=>{
    arr.forEach(i=>{
        if(i.id==id){
           i.children = [...(i.children || []),...children];
        } else {
            addDataToId(i.children|| [],children)
        }
    })
}

addDataToId(data,4,[{ id:'5',name: 'Child - 4' }]);

console.log(data);

,

正如在对 OP 问题的评论中已经建议的那样,可以选择一种递归方法,通过 Array.prototype.some 在内部迭代父节点的 children 数组,以实现早期退出(在数组迭代) 以及是否可以通过其布尔返回值添加 (a) 子节点(如果目标父节点存在)的指示。

还必须了解可能的子节点重复(通过它们的 id)以及如何处理它们。

// E.g. I want to find element with id='4'
// and add children: [{ id:'5',name: 'Child - 4' }]

const data = {
  id: 'root',children: [
    {
      id: '1',{
      id: '3',children: [
        {
          id: '4',};

function addChildrenToParentByParentId(id,node,childNodes) {
  let isSuccess = false;
  
  const { children } = node;
  if (node.id === id) {

    // because `childNodes` can be both a single node or a node list.
    childNodes = [].concat(childNodes);

    // prevent child node duplicates by ...
    node.children =
      ((Array.isArray(children) && children) || []).filter(child =>
      
        // ... skipping older nodes which feature newer ids ...
        childNodes.every(node => node.id !== child.id)

      // ... and always concatenating the most recent child nodes.
      ).concat(childNodes);

    isSuccess = true;

  } else if (Array.isArray(children)) {

    // recursion ... but using `Array.prototype.some`
    // in order to achieve both an early exit and the
    // indication whether children could be added.

    isSuccess = children.some(child =>
      addChildrenToParentByParentId(id,child,childNodes)
    );
  }
  return isSuccess;
}

// fails ... provides a node list
console.log(
  "addChildrenToParentByParentId('2',data,name: 'Child - 4' }]) ...",addChildrenToParentByParentId('2',name: 'Child - 4' }])
);
console.log(data);

// succeeds ... provides a single node
console.log(
  "addChildrenToParentByParentId('4',{ id:'5',name: 'Child - 4' }) ...",addChildrenToParentByParentId('4',name: 'Child - 4' })
);
console.log(data);

// succeeds ... provides a node list
console.log(
  "addChildrenToParentByParentId('3',[{ id:'6',name: 'Child - 4' },{ id:'7',addChildrenToParentByParentId('3',name: 'Child - 4' }])
);
console.log(data);

// succeeds ... provides a single node ... prevents duplicates
console.log(
  "addChildrenToParentByParentId('3',{ id:'6',name: 'Child - XX' }) ...",name: 'Child - XX' })
);
console.log(data);
.as-console-wrapper { min-height: 100%!important; top: 0; }

,

我更喜欢通过将树遍历与测试是否在当前节点上工作的谓词和更新节点的转换函数分开来编写这样的函数。

这是一个带有 alterTree 函数的版本,它执行树遍历,接受谓词函数和转换函数并返回一个接受对象的函数(.children 数组保持树结构) 并通过对与谓词匹配的所有节点调用转换函数并保持其他节点不变来转换所有与谓词匹配的节点,从而返回该对象的更改版本:

const alterTree = (pred,trans) => (o) =>
  pred (o)
    ? trans (o)
    : {...o,...(o .children ? {children: o .children .map (alterTree (pred,trans))} : {})}

const addChildren = (targetId,children) =>
  alterTree (
    ({id}) => id === targetId,(node) => ({...node,children: [... (node .children || []),... children]})
  )


const data = {id: 'root',children: [{id: '1',{id: '3',children: [{id: '4',name: 'Child - 4'}]}]};

console .log (addChildren ('4',[{id: '5',name: 'Child - 5'}]) (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

我们在 alterTree 的定义中调用 addChildren,传递一个报告 id 是否与我们的目标匹配的函数和一个添加节点的第二个函数。

请注意,alterTree 的工作是不变的。但是如果你传递给它的函数改变了它们的输入,就不能保证我们不会改变你的原始对象。 addChildren 传递的那些,但是不会改变任何东西,所以这个函数也不会改变原始数据。它返回应用了更改的对象的新副本。

如果我们愿意,我们可以更进一步,抽象出 "children" 用于我们的后代节点,但这有点离题了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?