从数组动态生成的嵌套树对象

Dynamically generated nested tree object from array

给定这两个字符串,

'1|100376|100377|100378''1|100376|100377|100379|100380'

我想创建一个如下所示的对象:

{
  id:"1",
  children: {
    id:"100376",
    children: {
      id:"100377",
      children: {
        id:"100378",
        children: {},
        id: "100379",
          children: {
            id:"100380",
            children: {}
        }
      }
    }
  }
}

这是我的代码,

var tree = {};

var addTree = function(aggregateId){
    if(aggregateId){
        var arr = aggregateId.split('|'),
            remainder = aggregateId.split('|').slice(1).join('|');
        arr.reduce(function(node,id){
            return node[id] || (node[id] = {});
        }, tree);
    }
};

addTree('1|100376|100377|100378');
addTree('1|100376|100377|100379|100380');

这会产生正确的嵌套,但不在键 idchildren 下。我该怎么做?

{
 "1": {
  "100376": {
   "100377": {
    "100378": {},
    "100379": {
     "100380": {}
    }
   }
  }
 }
}
var tree = {};

var addTree = function(aggregateId) {
    if (aggregateId) {
        var arr = aggregateId.split('|'),
        arr.reduce(function(node, id) {
            if (!node.id) {
                node.id = id;
                node.children = {};
            }
            return node.children;
        }, tree);
    }
};

addTree('1|100376|100377|100378');
addTree('1|100376|100377|100379|100380');
console.log(JSON.stringify(tree));
// {"id":"1","children":
//   {"id":"100376","children":
//     {"id":"100377","children":
//       {"id":"100379","children":
//         {"id":"100380","children":{}}}}}}

但是,我认为您可能存在设计缺陷 - children 只能托管一个 child。您确定不希望它是一个包含节点的数组,或者更好的是 object 按键索引节点,就像您的原始情况一样?它适用于您的原始情况,因为您有从 ids 到 children 的映射;但在您提议的结构中,children 只是一个 child,总是。

EDIT 更改要求:这有点丑陋,因为 Array.prototype.find 仍然不普遍可用。可以使用 .filter()[0] 作为 hack,但效率很低。所以:

var tree = [];

var addTree = function(aggregateId) {
    if (aggregateId) {
        var arr = aggregateId.split('|'),
        arr.reduce(function(children, id) {
            var node, i;
            for (i = 0; i < children.length; i++) {
                if (children[i].id == id) {
                  node = children[i];
                  break;
                }
            }
            if (!node) {
                children.push(node = {
                    id: id,
                    children: []
                });
            }
            return node.children;
        }, tree);
    }
};

你已经完成了一半。您需要做的就是post-处理您的输出以创建所需的表单,这就像

一样简单
function makeTree(tree) {
  if (!tree) return [];
  return Object.keys(tree) . map(function(key) {
      return { id: key, children: makeTree(tree[key]) };
  });
}

这会输出一个在数组中包含子项的结构,这是您需要的,以防有多个子项,正如另一个 poster 指出的那样。输出是:

{
  id:"1",
  children: [
    {
      id:"100376",
      children: [
        {
          id:"100377",
          children: [
            {
              id:"100378", 
              children: []
            },
            {
              id: "100379",
              children: [
                {
                  id:"100380",
                  children: []
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

以下构建中间表示的代码可能比您编写的代码更简洁:

function addTree(tree, input) {

  function addNodes(node, ids) {
    if (!ids.length) return;                  // exit recursion when done
    var id = ids.shift();                     // pick off next id
    var subnode = node[id] = node[id] || { }; // find or create node
    addNodes(subnode, ids);                   // recursively update 
  }

  addNodes(tree, input.split('|'));
}

其工作方式是内部函数 addNodes 使用数组 ids 中的 ID 更新 node。它检查以确保有更多的 id 要添加,然后选择第一个,查看相应的节点是否已经存在,如果没有创建一个,然后将此 id 添加到该子节点。

对您要处理的所有字符串继续调用 addTree。然后,post-处理tree得到最终的输出。

使用这个:

> var input1 = '1|100376|100377|100378';
> var input2 = '1|100376|100379|100380';
> var tree = {};

> addTree(tree, input1) // add initial input
> tree
<"{
    "1": {
      "100376": {
        "100377": {
          "100378": {}
        }
      }
    }
  }"

> addTree(tree, input2) // add some more input
> tree
< "{
    "1": {
      "100376": {
        "100377": {
          "100378": {}
        },
        "100379": {
          "100380": {}
        }
      }
    }
  }"

> makeTree(tree)
< "[
    {
      "id": "1",
      "children": [
        {
          "id": "100376",
          "children": [
            {
              "id": "100377",
              "children": [
                {
                  "id": "100378",
                  "children": []
                }
              ]
            },
            {
              "id": "100379",
              "children": [
                {
                  "id": "100380",
                  "children": []
                }
              ]
            }
          ]
        }
      ]
    }
  ]"

你的问题描述的不是很清楚。但是,如果您想要一种简单的方法来生成树,您所要做的就是在竖线字符 ("|") 处拆分字符串,然后向后读取数组。

如果你想有多个children,你可以很容易地调整下面的逻辑使.children成为一个数组。然后,您所要做的就是 o.children.push(p) 而不是 o.children = p.

由于无法在 JavaScript 中 right-reduce,您可以 reverse the array first, before reduce-ing

var aggregate = function (s) {
    return s.split("|").reverse().reduce(function (p, c) {
        var o = {};
        o.id = c;
        o.children = p;
        return o;
    }, {});
}

console.log(aggregate("'1|100376|100377|100378'"));