【问题标题】:Form a nested tree from an array of objects in javascript从javascript中的对象数组形成嵌套树
【发布时间】:2021-09-27 17:53:48
【问题描述】:

所以有以下格式的对象数组

let inputs = [
    {
      "id": "614344d9d9c21c0001e6af2e",
      "groupName": "Unassigned",
      "parentGroup": "null"
    },
    {
      "id": "614447da152f69c3c1d52f2e",
      "groupName": "P1",
      "parentGroup": "null"
    },
    {
      "id": "614447da152f69c3c1d52f38",
      "groupName": "K1",
      "parentGroup": "C1"
    },
    {
      "id": "614447da152f69c3c1d52f3e",
      "groupName": "A2",
      "parentGroup": "C2"
    },
    {
      "id": "614447da152f69c3c1d52f40",
      "groupName": "G1",
      "parentGroup": "P2"
    },
    {
      "id": "614447da152f69c3c1d52f46",
      "groupName": "F1",
      "parentGroup": "null"
    },
    {
      "id": "614447da152f69c3c1d52f30",
      "groupName": "P2",
      "parentGroup": "null"
    },
    {
      "id": "614447da152f69c3c1d52f36",
      "groupName": "C2",
      "parentGroup": "P1"
    },
    
    {
        "id": "614447da152f69c3c1d52f3c",
        "groupName": "A1",
        "parentGroup": "C2"
      },
      {
        "id": "614447da152f69c3c1d52f34",
        "groupName": "C1",
        "parentGroup": "P1"
      },
      {
        "id": "614447da152f69c3c1d52f32",
        "groupName": "P3",
        "parentGroup": "null"
      },
      {
        "id": "614447da152f69c3c1d52f3a",
        "groupName": "K2",
        "parentGroup": "C1"
      },
      {
        "id": "614447da152f69c3c1d52f42",
        "groupName": "GG1",
        "parentGroup": "G1"
      },
      {
        "id": "614447da152f69c3c1d52f44",
        "groupName": "GGG1",
        "parentGroup": "GG1"
      }
  ]

我正在尝试创建格式的树结构

{name:'p1',children:[{name:'c1':children:[]}]}

所以我对给定数组的所有元素进行了排序,考虑到 parentGroup 为“null”的元素位于数组的顶部。

 let finalArr = [];
 inputs.sort((a,b)=> (a.parentGroup === "null") ? -1 : 1);

对于输入数组的每个元素,我在逻辑下进行迭代

inputs.forEach(ele => {
            if(ele.parentGroup === "null"){
             let child= {name:ele.groupName,children:[]};
             finalArr.push(child);
            }else{
              finalArr.forEach(item => {
                this.findNode(item,ele);
              })
            }
          });

如果元素的'parentGroup'为“null”,则创建一个叶子类型的obj并将元素推送到'finalArr'数组

否则,然后通过递归函数遍历 'finalArr' 的所有元素

public findNode(ele, obj){
    if(ele.children.length === 0){
      if(ele.name === obj.parentGroup){
        let child = {name:obj.groupName, children:[]};
      ele.children.push(child);
      }
    }else{
      let j = ele.children.length-1;
      this.findNode(ele.children[j--],obj);
    }
  }

此递归函数将检查元素是否有子元素,如果没有子元素,则将给定 obj 的 parentGroup 与“FinalArr”中的元素名称进行比较。 如果是,则将当前 obj 推送给 finalArr 元素的子元素。

else,即当children有更多元素时,会触发相同的递归,直到达到元素的深度。

有了这个,我想我会用给定的输入数组创建一个树结构,但是当父母有更多相同级别的孩子时,这个逻辑会失败, 所以输入数组有'c1',它是'p1'的孩子,但只有孩子'c2'驻留,不确定我错过了什么。

【问题讨论】:

  • 请仅解决此问题中的一个问题。您在获取树数据结构或在 html 中呈现树时遇到问题吗?
  • 获取树状数据结构

标签: javascript arrays tree


【解决方案1】:

您可以采用标准算法来获取具有给定数据的树

const
    getTree = (data, id, parent, root, fn = o => o) => {
      var t = {};
      data.forEach(o => ((t[o[parent]] ??= {}).children ??= []).push(Object.assign(t[o[id]] = t[o[id]] || {}, fn(o))));
      return t[root].children;
    },
    data = [{ id: "614344d9d9c21c0001e6af2e", groupName: "Unassigned", parentGroup: "null" }, { id: "614447da152f69c3c1d52f2e", groupName: "P1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f38", groupName: "K1", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f3e", groupName: "A2", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f40", groupName: "G1", parentGroup: "P2" }, { id: "614447da152f69c3c1d52f46", groupName: "F1", parentGroup: "null" }, { id: "614447da152f69c3c1d52f30", groupName: "P2", parentGroup: "null" }, { id: "614447da152f69c3c1d52f36", groupName: "C2", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f3c", groupName: "A1", parentGroup: "C2" }, { id: "614447da152f69c3c1d52f34", groupName: "C1", parentGroup: "P1" }, { id: "614447da152f69c3c1d52f32", groupName: "P3", parentGroup: "null" }, { id: "614447da152f69c3c1d52f3a", groupName: "K2", parentGroup: "C1" }, { id: "614447da152f69c3c1d52f42", groupName: "GG1", parentGroup: "G1" }, { id: "614447da152f69c3c1d52f44", groupName: "GGG1", parentGroup: "GG1" }],
    tree = getTree(data, 'groupName', 'parentGroup', null, ({ groupName: name }) => ({ name }));

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 感谢您的快速解决方案,但我试图获得类似 [{name:'p1',children:[{name:'c1':children:[] 的最终格式数组}]},{name:'F1',children:[]},{name:'P3',children:[]}]
【解决方案2】:

我认为问题在于如何使用finalArr 来生成 html 元素。

在执行console.log(finalArr) 时,它看起来像下面的代码块。所以在我看来,构建finalArr 结构的代码工作正常。

// console.log(finalArr)

[
  { "name": "P3", "children": [] },
  {
    "name": "P2",
    "children": [
      {
        "name": "G1",
        "children": [
          { "name": "GG1", "children": [
              { "name": "GGG1", "children": [] }
            ]
          }
        ]
      }
    ]
  },
  { "name": "F1", "children": [] },
  {
    "name": "P1",
    "children": [
      { "name": "C2", "children": [
          { "name": "A1", "children": [] }
        ]
      }
    ]
  },
  { "name": "Unassigned", "children": [] }
]

编辑

正如评论 C1 中提到的 OP 丢失了。我引入了一个根元素,它将finalArr 作为其子元素,并将 findNode 更改为使用 for 循环而不是 forEach。这样我们也可以在找到节点时中断,不再继续递归。

作为初始排序的一部分,我们将按 parentGroup 对输入进行排序,因此我们确保在尝试使用 findNode 找到它之前将子父级添加到树结构中。

我相信这会产生正确的结果:


inputs.sort((a, b) => (a.parentGroup === "null" ? -1 : 1));

// Sort by parentGroup
let root = inputs.pop();
let inputsDescending = [root];
let max = inputs.length * inputs.length;
let c = 0;
while (inputs.length > 0 && max > c) {
  const child = inputs.pop();
  const hasParentGroup = inputsDescending.find(
    (parent) => parent.groupName === child.parentGroup
  );
  if (hasParentGroup || child.parentGroup === "null") {
    inputsDescending.push(child);
  } else {
    inputs.unshift(child);
  }
}

let rootEle = { name: "root", children: [] };

inputsDescending.forEach((obj) => {
  if (obj.parentGroup === "null") {
    let child = { name: obj.groupName, children: [] };
    rootEle.children.push(child);
  } else {
    findNode(rootEle, obj);
  }
});

function findNode(ele, obj) {
  if (ele.name === obj.parentGroup) {
    let child = { name: obj.groupName, children: [] };
    ele.children.push(child);
    return true;
  } else {
    const c = ele.children.length;
    if (c > 0) {
      for (let i = 0; c > i; i++) {
        const found = findNode(ele.children[i], obj);
        if (found) break;
      }
    }
  }
}

const finalArr = rootEle.children;

现在finalArr 看起来像这样:

[
  { "name": "Unassigned", "children": [] },
  {
    "name": "P1",
    "children": [
      {
        "name": "C1",
        "children": [
          { "name": "K1", "children": [] },
          { "name": "K2", "children": [] }
        ]
      },
      {
        "name": "C2",
        "children": [
          { "name": "A2", "children": [] },
          { "name": "A1", "children": [] }
        ]
      }
    ]
  },
  { "name": "F1", "children": [] },
  {
    "name": "P2",
    "children": [
      { "name": "G1", "children": [
          { "name": "GG1", "children": [] }
        ]
      }
    ]
  },
  { "name": "P3", "children": [] }
]

【讨论】:

  • 是的,这个 finalArr 缺少 'P1' 下的子节点,如果你注意到 'inputs' 数组,有一个元素 { "id": "614447da152f69c3c1d52f34", "groupName": "C1" , "parentGroup": "P1" },所以有了这个,'P1'应该有'C1',就像'C2'在P1下的样子
  • 嗨@Lisa,我没有意识到C1 不见了。我在答案中添加了一个编辑部分,我相信代码现在正在产生正确的结果。
  • 嗨@Jens Ravn Jensen,衷心感谢您的回答,但我注意到嵌套的孩子'C1'也有两个孩子'K1'和'K2'。'C2'一样,有“A1”和“A2”,用这个解决方案,我只能得到 1 个孩子
  • 你好@Lisa,我错过了。这是因为输入的顺序。当我们尝试为 A2 查找节点时,我们尚未将其父 C2 添加到树结构中。我添加了一个按 parentGroup 部分排序,以便在我们调用 findNode 之前输入将是从祖先到后代的顺序。
  • 再次感谢@Jens Ravn Jensen,使用更新后的逻辑,给定“输入”数组中的最后一个对象丢失了,这里“GGG1”丢失了,所以我添加了一个虚拟元素让 dummyEle = { "id": "614447da152f69c3c1d52f44", "groupName": "", "parentGroup": "" } result.body.push(dummyEle);,然后继续你分享的逻辑,它有效,再次感谢您的解决方案.
猜你喜欢
  • 2017-06-14
  • 1970-01-01
  • 2015-08-02
  • 2018-05-08
  • 2020-12-07
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 2019-09-23
相关资源
最近更新 更多