简体   繁体   English

将父子数组转换为树

[英]Convert parent-child array to tree

Can anyone help converting the following list of parent-child objects: 任何人都可以帮助转换以下父子对象列表:

[
   {
      "name":"root",
      "_id":"root_id",
   },
   {
      "name":"a1",
      "parentAreaRef":{
         "id":"root_id",
      },
      "_id":"a1_id",
   },
   {
      "name":"a2",
      "parentAreaRef":{
         "id":"a1_id",
      },
      "_id":"a2_id",
   },
   {
      "name":"a3",
      "parentAreaRef":{
         "id":"a2_id",
      },
      "_id":"a3_id",
   },
   {
      "name":"b1",
      "parentAreaRef":{
         "id":"root_id",
      },
      "_id":"b1_id",
   },
   {
      "name":"b2",
      "parentAreaRef":{
         "id":"b1_id",
      },
      "_id":"b2_id",
   },
   {
      "name":"b3",
      "parentAreaRef":{
         "id":"b1_id",
      },
      "_id":"b3_id",
   }
]

into a tree structure showing the parent-child relationship: 显示父子关系的树结构:

[
    {
        "name": "root",
        "_id":"root_id",
        "children": [
            {
                "name": "a1",
                "_id":"a1_id",
                "children" : [
                    {
                        "name" : "a2",
                        "_id":"a2_id",
                        "children" : [
                            {
                                "name" : "a3"
                                "_id":"a3_id"
                            }
                        ]
                    }
                ]
            }, 
            {
                "name": "b1",
                "_id":"b1_id",
                "children" : [
                    {
                        "name" : "b2"
                        "_id":"b2_id"
                    },
                    {
                        "name" : "b3"
                        "_id":"b3_id"
                    }
                ]
            }
        ]
    }
]

(The output structure is an array to allow for multiple roots but if we can get a solution that handles a single root that's great too.) (输出结构是一个允许多个根的数组,但是如果我们能够得到一个处理单个根的解决方案,那也很棒。)

The output tree looks like this: 输出树如下所示:

root
  |
  -- a1
  |   |
  |   -- a2
  |       |
  |       -- a3
  | 
  -- b1
      |
      -- b2
      -- b3

Thanks! 谢谢!

I have a solution that works. 我有一个有效的解决方案。 I can give you hints as far as solving it. 就解决问题我可以给你提示。 The good thing is that your data doesn't contain any forward references to nodes. 好处是您的数据不包含任何对节点的前向引用。 So you can create your tree with just one pass through the array. 因此,只需一次通过数组即可创建树。 If note, you will need to make a pass through the entire array first to build up a map of ids to nodes. 如果需要注意,您需要首先遍历整个数组,以构建节点的ID映射。

Your algorithm will look like this. 您的算法将如下所示。

  1. Create a map that maps id's to nodes. 创建一个将id映射到节点的映射。 This will make it easy to look up nodes. 这样可以轻松查找节点。
  2. Loop through the array of nodes. 循环遍历节点数组。
  3. For each element. 对于每个元素。
    1. Add an entry into the map. 在地图中添加一个条目。
    2. Add a children property (an array) to this node. children属性(数组)添加到此节点。
    3. Does the element have a parent? 元素是否有父母? If not it must be the root, so assign the this element to the root of the tree. 如果不是,则必须是根,因此将此元素分配给树的根。
    4. This element has a parent, so look up the parent node, and then add this current node as a child of the parent node (add it to the children array). 此元素具有父元素,因此请查找父节点,然后将此当前节点添加为父节点的子节点(将其添加到children数组)。

This should help you solve the problem. 这应该可以帮助您解决问题。 If you're having specific issues with this algorithm I can point out where the problems are and how to solve it or post the solution and explain how I solved it. 如果您遇到此算法的具体问题,我可以指出问题所在以及如何解决问题或发布解决方案并解释我是如何解决的。

UPDATE UPDATE

I looked at the solution that you have. 我看了你的解决方案。 You actually don't need recursion for this and you can do this iteratively using the algorithm I described above. 实际上你不需要递归,你可以使用我上面描述的算法迭代地执行此操作。 You are also modifying the structure in-place, which makes the algorithm more complicated. 您还在原地修改结构,这使得算法更复杂。 But you're somewhat on the right track. 但是你有点走上正轨。 Here is how I solved it: 这是我解决它的方式:

var idToNodeMap = {}; //Keeps track of nodes using id as key, for fast lookup
var root = null; //Initially set our loop to null

//loop over data
data.forEach(function(datum) {

    //each node will have children, so let's give it a "children" poperty
    datum.children = [];

    //add an entry for this node to the map so that any future children can
    //lookup the parent
    idToNodeMap[datum._id] = datum;

    //Does this node have a parent?
    if(typeof datum.parentAreaRef === "undefined") {
        //Doesn't look like it, so this node is the root of the tree
        root = datum;        
    } else {        
        //This node has a parent, so let's look it up using the id
        parentNode = idToNodeMap[datum.parentAreaRef.id];

        //We don't need this property, so let's delete it.
        delete datum.parentAreaRef;

        //Let's add the current node as a child of the parent node.
        parentNode.children.push(datum);        
    }
});

Now root points to the entire tree. 现在root指向整个树。

Fiddle . 小提琴

For the case where the array of elements is in arbitrary order, you will have to initialize idToNodeMap first. 对于元素数组按任意顺序排列的情况,您必须首先初始化idToNodeMap The rest of the algorithm remains more-or-less the same (except for the line where you store the node in the map; that's not needed because you did it already in the first pass): 算法的其余部分或多或少保持不变(除了您在地图中存储节点的行;这不是必需的,因为您已经在第一次传递中执行了此操作):

var idToNodeMap = data.reduce(function(map, node) {
    map[node._id] = node;
    return map;
}, {});

I know it's late, but I just finished this algorithm and maybe it can help some other people looking to solve the same problem: http://jsfiddle.net/ akerbeltz /9dQcn/ 我知道现在已经很晚了,但我刚刚完成了这个算法,也许它可以帮助其他人寻求解决同样的问题: http//jsfiddle.net/ akerbeltz / 9dQcn /

A good thing about it is that it doesn't requires any special sort on the original object. 它的一个好处是它不需要对原始对象进行任何特殊排序。

If you need to adapt it to your needs change the following lines: 如果您需要根据需要进行调整,请更改以下行:

  1. Change the _id and the parentAreaRef.id depending on your structure. 根据您的结构更改_id和parentAreaRef.id。

    if (String(tree[i]._id) === String(item.parentAreaRef.id)) {

  2. Change the parentAreaRef depending on your structure. 根据您的结构更改parentAreaRef。

    if (tree[idx].parentAreaRef) buildTree(tree, tree.splice(idx, 1)[0])

Hope it helps! 希望能帮助到你!

UPDATE UPDATE

Adding code here based on @Gerfried comment: 根据@Gerfried评论在此处添加代码:

var buildTree = function(tree, item) {
    if (item) { // if item then have parent
        for (var i=0; i<tree.length; i++) { // parses the entire tree in order to find the parent
            if (String(tree[i]._id) === String(item.parentAreaRef.id)) { // bingo!
                tree[i].childs.push(item); // add the child to his parent
                break;
            }
            else buildTree(tree[i].childs, item); // if item doesn't match but tree have childs then parses childs again to find item parent
        }
    }
    else { // if no item then is a root item, multiple root items are supported
        var idx = 0;
        while (idx < tree.length)
            if (tree[idx].parentAreaRef) buildTree(tree, tree.splice(idx, 1)[0]) // if have parent then remove it from the array to relocate it to the right place
            else idx++; // if doesn't have parent then is root and move it to the next object
    }
}

for (var i=0; i<data.length; i++) { // add childs to every item
    data[i].childs = [];
}
buildTree(data);
console.log(data);

Thanks! 谢谢!

I know I'm too late, but since I just finished my contribution to a sample implementation of how this can be done I thought I would share it, since it might be found useful / or give inspiration to an alternative solution. 我知道我已经太晚了,但是因为我刚刚完成了对如何实现这一目标的示例实施的贡献,我想我会分享它,因为它可能被认为是有用的/或者给予替代解决方案灵感。

The implementation can be found here: http://jsfiddle.net/sw_lasse/9wpHa/ 可以在这里找到实现: http//jsfiddle.net/sw_lasse/9wpHa/

The main idea of the implementation centers around the following recursive function: 实现的主要思想围绕以下递归函数:

// Get parent of node (recursive)
var getParent = function (rootNode, rootId) {

    if (rootNode._id === rootId)
        return rootNode;

    for (var i = 0; i < rootNode.children.length; i++) {
        var child = rootNode.children[i];
        if (child._id === rootId)
            return child;

        if (child.children.length > 0)
            var childResult = getParent(child, rootId);

        if (childResult != null) return childResult;
    }
    return null;
};

... that is used to build the tree. ...用于构建树。

您可以使用npm中的array-to-tree模块。

Borrowing the caching logic from Vivin Paliath's answer, I have created a reusable function to convert a list of data with child-parent relationships into a tree. 借用Vivin Paliath的答案中的缓存逻辑,我创建了一个可重用的函数来将具有子父关系的数据列表转换为树。

 var data = [ { "id" : "root" }, { "id" : "a1", "parentId" : "root", }, { "id" : "a2", "parentId" : "a1", }, { "id" : "a3", "parentId" : "a2", }, { "id" : "b1", "parentId" : "root", }, { "id" : "b2", "parentId" : "b1", }, { "id" : "b3", "parentId" : "b1", } ]; var options = { childKey : 'id', parentKey : 'parentId' }; var tree = walkTree(listToTree(data, options), pruneChildren); document.body.innerHTML = '<pre>' + JSON.stringify(tree, null, 4) + '</pre>'; function listToTree(list, options) { options = options || {}; var childKey = options.childKey || 'child'; var parentKey = options.parentKey || 'parent'; var childrenKey = options.childrenKey || 'children'; var nodeFn = options.nodeFn || function(node, name, children) { return { name : name, children : children }; }; var nodeCache = {}; return list.reduce(function(tree, node) { node[childrenKey] = []; nodeCache[node[childKey]] = node; if (typeof node[parentKey] === 'undefined' || node[parentKey] === '') { tree = nodeFn(node, node[childKey], node[childrenKey]); } else { parentNode = nodeCache[node[parentKey]]; parentNode[childrenKey].push(nodeFn(node, node[childKey], node[childrenKey])); } return tree; }, {}); } function walkTree(tree, visitorFn, parent) { if (visitorFn == null || typeof visitorFn !== 'function') { return tree; } visitorFn.call(tree, tree, parent); if (tree.children && tree.children.length > 0) { tree.children.forEach(function(child) { walkTree(child, visitorFn, tree); }); } return tree; } function pruneChildren(node, parent) { if (node.children.length < 1) { delete node.children; } } 

Give it a try: 试试看:

   var obj = {};
   obj.rootElements = [];
   var currentRoot;
   var currentParent;
   for (s in a) {
       var t = a[s];
       var id = t._id;
       if (t.parentAreaRef) {
           var parentId = t.parentAreaRef.id;
           if (parentId == currentParent._id) {
               //add children
               if (!currentParent.children) {
                   currentParent.children = [];
               }
               currentParent.children.push(t);
           }
           else {
               addChildToParent(t, parentId);
           }

       }
       else // is root
       {
           currentRoot = t;
           currentParent = t;
           obj.rootElements.push(currentRoot);
       }
   }

   var t = currentRoot

   function addChildToParent(child, parentId, root) {
       for (p in a) {
           if (a[p]._id.toString() == parentId.toString()) {
               if (!a[p].children) {
                   a[p].children = [];
               }
               a[p].children.push(t);
           }
       }
   }

There is an error in your string 您的字符串中有错误

a[p].children.push(t);

It should be 它应该是

a[p].children.push(child);

also I'm little optimize it: 我也很少优化它:

var data = [{"id":1,"name":"X","parentId":null},{"id":2,"name":"Y","parentId":1},{"id":3,"name":"D","parentId":2},{"id":2,"name":"S","parentId":1},{"id":5,"name":"K","parentId":4}]
    var obj = {};
    obj.rootElements = [];
    for (i in data) {
        var _elem = data[i];
        if (_elem.parentId) {
            var _parentId = _elem.parentId;
            if (_parentId == _elem.id) {
                // check children, if false - add
                if (!_elem.children) {
                    _elem.children = [];
                }
                _elem.children.push(_elem);
            }
            else {
                addChildToParent(_elem, _parentId);
            }
        }
        else // is root
        {
            obj.rootElements.push(_elem);
        }
    }
    function addChildToParent(child, parentId, root) {
        for (j in data) {
            if (data[j].id.toString() == parentId.toString()) {
                if (!data[j].children) {
                    data[j].children = [];
                }
                data[j].children.push(child);
            }
        }
    }
    res.send(obj.rootElements); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM