【问题标题】:Produce a nested object from array of objects with flat path从具有平坦路径的对象数组中生成嵌套对象
【发布时间】:2021-02-16 17:15:24
【问题描述】:

我正在尝试将一个对象数组(数组中的每个对象都有一个路径和 id)转换为代表该路径的树。例如,对于[{path: 'foo/bar/baz', id: 1}]的给定输入,输出如下所示

[
    {
      "path": "foo",
      "children": [
        {
          "path": "bar",
          "children": [
            {
              "path": "baz",
              "children": [],
            }
          ]
        }
      ]
    }
]

到目前为止,我有以下代码:


const pathObjs = [
    { id: 1, path: 'foo/bar/baz' },
  ];

const result = [];
const level = { result };

for (p of pathObjs) {
  p.path.split('/').reduce((r, path) => {
    if (!r[path]) {
      r[path] = { result: [] };

      const p = { path, children: r[path].result };

      r.result.push(p);
    }

    return r[path];
  }, level);
}

我不知道如何在正确的级别分配每个路径的id,以便最终结果如下所示:

[
    {
      "path": "foo",
      "children": [
        {
          "path": "bar",
          "children": [
            {
              "path": "baz",
              "children": [],
              // how to add this guy here!
              "id": 1,
            }
          ]
        }
      ]
    }
]

请有人将我推向正确的方向。

【问题讨论】:

    标签: javascript algorithm tree


    【解决方案1】:

    不要将const p 设为reduce 回调中的局部变量,而是使用在reduce 完成后可访问的变量,以便您可以访问最后分配的对象以添加附加属性(或属性)。

    所以改变:

          const p = { path, children: r[path].result };
          r.result.push(p);
    

    收件人:

          last = { path, children: r[path].result };
          r.result.push(last);
    

    并将last 定义为for 块范围内的局部变量。在reduce 完成后,您可以使用Object.assign 改变那个“叶子”对象,添加额外的属性:

      Object.assign(last, rest); // rest is whatever needs to be added.
    

    所以你的代码可以修改如下:

    const result = [];
    const level = { result };
    
    for (p of pathObjs) {
      let last; // This will be the latest added object
      let { path, ...rest } = p; // Extract the path and the other properties
      path.split('/').reduce((r, path, i, {length}) => {
        if (!r[path]) {
          r[path] = { result: [] };
    
          last = { path, children: r[path].result };
    
          r.result.push(last);
        }
    
        return r[path];
      }, level);
      Object.assign(last, rest); // Extend with the extra properties.
    }
    

    【讨论】:

    • 非常感谢?
    【解决方案2】:

    此解决方案会重组您的原始代码。主要区别在于路径列表是反向的,所以我们从叶节点而不是根节点开始。

    有一个明确的检查来查看我们是否在第一个索引处(我们可以假设它是叶子)。或者,您可以改为检查是否定义了 result,如果您愿意,可以依赖它而不是索引。

    const pathObjects = [{ id: 1, path: "foo/bar/baz" }];
    
    const result = [];
    
    for (let { id, path } of pathObjects) {
      const obj = path
        .split("/")
        .reverse()
        .reduce((result, path, index) => {
          const node = {
            path,
            children: result ? [result] : []
          };
    
          if (index === 0) {
            node.id = id;
          }
    
          return node;
        }, null);
    
      result.push(obj);
    }
    
    console.log(result);

    【讨论】:

    • 嗯,我明白了。尽管上述代码的输出具有重复的父节点。例如输入[{ id: 1, path: "foo/bar/baz" }, { id: 2, path: "foo/bar/bax" }] 而不是foo.bar 有两个孩子[baz, baz],有两个单独的foo.bar 有两个不同的孩子?
    猜你喜欢
    • 2021-12-06
    • 2021-07-04
    • 2020-08-01
    • 2019-09-27
    • 2021-01-14
    • 2021-03-22
    • 1970-01-01
    • 2020-11-18
    • 2021-07-12
    相关资源
    最近更新 更多