【问题标题】:How to create a tree (parent-child) object from Array in Javascript如何在Javascript中从数组创建树(父子)对象
【发布时间】:2020-03-03 17:27:21
【问题描述】:

我有一个类似的数组

[
  "parent1|child1|subChild1",
  "parent1|child1|subChild2",
  "parent|child2|subChild1",
  "parent1|child2|subChild2",
  "parent2|child1|subChild1",
  "parent2|child1|subChild2",
  "parent2|child2|subChild1",
.
.
.    
]

| 之前的第一个字符串是父字符串,| 之前的第二个字符串是是孩子,第二个| 之后的第三个字符串是子孩子

如何将这个数组转换成类似的对象

[
 {
  "id": "parent1",
  "children":[
   {
    "id": "child1",
    "children":[
     {
      "id": "subChild1"
     }
    ]
   }
  ]
 }
]

父->子->子子对象

根据 Sebastian 的回答,我在下面尝试使用 typescript

private genTree(row) {
        let self = this;
        if (!row) {
            return;
        }
        const [parent, ...children] = row.split('|');
        if (!children || children.length === 0) {
            return [{
                id: parent,
                children: []
            }];
        }
        return [{
            id: parent,
            children: self.genTree(children.join('|'))
        }];
    }

    private mergeDeep(children) {
        let self = this;
        const res = children.reduce((result, curr) => {
            const entry = curr;
            const existing = result.find((e) => e.id === entry.id);
            if (existing) {
                existing.children = [].concat(existing.children, entry.children);
            } else {
                result.push(entry);
            }
            return result;
        }, []);
        for (let i = 0; i < res.length; i++) {
            const entry = res[i];
            if (entry.children && entry.children.length > 0) {
                entry.children = self.mergeDeep(entry.children);
            }
        };
        return res;
    }

private constructTree(statKeyNames){
    let self = this;
    const res = this.mergeDeep(statKeyNames.map(self.genTree).map(([e]) => e));
    console.log(res);
}

但这给了我:

无法读取未定义的属性“genTree”错误

更新:

根据 Sebastian 的评论,将 self.genTree 更改为 this.genTree.bind(this) 并且没有任何问题

【问题讨论】:

    标签: javascript arrays typescript nested


    【解决方案1】:

    您可以使用 mapper 对象将每个对象映射到其唯一路径(您可以将对象映射到每个 id,但 id 在这里不是唯一的)。然后reduce 数组中的每个部分项。将root 对象设置为initialValue。累加器将是当前项目的父对象。在每次迭代中返回当前对象。

    const input = [
        "parent1|child1|subChild1",
        "parent1|child1|subChild2",
        "parent1|child2|subChild1",
        "parent1|child2|subChild2",
        "parent2|child1|subChild1",
        "parent2|child1|subChild2",
        "parent2|child2|subChild1"
      ],
      mapper = {},
      root = { children: [] }
    
    for (const str of input) {
      let splits = str.split('|'),
          path = '';
    
      splits.reduce((parent, id, i) => {
        path += `${id}|`;
    
        if (!mapper[path]) {
          const o = { id };
          mapper[path] = o; // set the new object with unique path
          parent.children = parent.children || [];
          parent.children.push(o)
        }
        
        return mapper[path];
      }, root)
    }
    
    console.log(root.children)

    【讨论】:

    • 由于某些原因,这种方法适用于所有场景并且性能更高。
    • @AshishNandanSingh 因为每个级别都没有嵌套循环和find()。它仅取决于 mapper 对象。每个部分路径(如parent1|parent1|child1|parent2|child2|subChild1)都会被添加,并且对对象的引用作为值保留。每当该已映射路径的新孩子出现时,我们只需要访问它并将其添加为它的孩子。如果它不存在,我们将路径添加为键。您可以检查 mapper 对象以了解其工作原理。
    【解决方案2】:

    您必须为此使用递归。看这里:

    const arr = [
      "parent1|child1|subChild1",
      "parent1|child1|subChild2",
      "parent|child2|subChild1",
      "parent1|child2|subChild2",
      "parent2|child1|subChild1",
      "parent2|child1|subChild2",
      "parent2|child2|subChild1"
    ];
    
    function genTree(row) {
    
      const [parent, ...children] = row.split('|');
    
      if (!children || children.length === 0) {
        return [{
          id: parent,
          children: []
        }];
      }
    
      return [{
        id: parent,
        children: genTree(children.join('|'))
      }];
    };
    
    function mergeDeep(children) {
    
      const res = children.reduce((result, curr) => {
    
        const entry = curr;
    
        const existing = result.find((e) => e.id === entry.id);
        if (existing) {
    
          existing.children = [].concat(existing.children, entry.children);
        } else {
          result.push(entry);
        }
    
        return result;
      }, []);
    
      for (let i = 0; i < res.length; i++) {
    
        const entry = res[i];
        if (entry.children && entry.children.length > 0) {
          entry.children = mergeDeep(entry.children);
        }
      };
    
      return res;
    }
    
    const res = mergeDeep(arr.map(genTree).map(([e]) => e));
    console.log(JSON.stringify(res, false, 2));

    我在这里使用了两个助手:genTree(row) 从每一行递归生成一个简单的树,mergeDeep(children) 减少了arr.map(genTree).map(([e]) =&gt; e) 的结果中的第一级树,然后迭代数组并递归执行每个条目的所有children都一样。

    【讨论】:

    • 我正在尝试将其转换为 typescript.. 但我收到错误消息“无法读取未定义的属性 'genTree'”
    • 你的尝试是什么?没有任何产生错误的代码很难说
    • 啊,你没有定义self,试试简单的this.genTree
    • “this”出现错误,然后我添加了“self”。问题中遗漏了它,我现在也添加了问题
    • constructTree(...) 方法是如何被调用的?您能否也发布调用此方法的代码?
    猜你喜欢
    • 2018-05-15
    • 1970-01-01
    • 2017-07-14
    • 2023-03-05
    • 2018-08-30
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 2020-03-16
    相关资源
    最近更新 更多