簡體   English   中英

將父子數組轉換為樹

[英]Convert parent-child array to tree

任何人都可以幫助轉換以下父子對象列表:

[
   {
      "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",
   }
]

顯示父子關系的樹結構:

[
    {
        "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"
                    }
                ]
            }
        ]
    }
]

(輸出結構是一個允許多個根的數組,但是如果我們能夠得到一個處理單個根的解決方案,那也很棒。)

輸出樹如下所示:

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

謝謝!

我有一個有效的解決方案。 就解決問題我可以給你提示。 好處是您的數據不包含任何對節點的前向引用。 因此,只需一次通過數組即可創建樹。 如果需要注意,您需要首先遍歷整個數組,以構建節點的ID映射。

您的算法將如下所示。

  1. 創建一個將id映射到節點的映射。 這樣可以輕松查找節點。
  2. 循環遍歷節點數組。
  3. 對於每個元素。
    1. 在地圖中添加一個條目。
    2. children屬性(數組)添加到此節點。
    3. 元素是否有父母? 如果不是,則必須是根,因此將此元素分配給樹的根。
    4. 此元素具有父元素,因此請查找父節點,然后將此當前節點添加為父節點的子節點(將其添加到children數組)。

這應該可以幫助您解決問題。 如果您遇到此算法的具體問題,我可以指出問題所在以及如何解決問題或發布解決方案並解釋我是如何解決的。

UPDATE

我看了你的解決方案。 實際上你不需要遞歸,你可以使用我上面描述的算法迭代地執行此操作。 您還在原地修改結構,這使得算法更復雜。 但是你有點走上正軌。 這是我解決它的方式:

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);        
    }
});

現在root指向整個樹。

小提琴

對於元素數組按任意順序排列的情況,您必須首先初始化idToNodeMap 算法的其余部分或多或少保持不變(除了您在地圖中存儲節點的行;這不是必需的,因為您已經在第一次傳遞中執行了此操作):

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

我知道現在已經很晚了,但我剛剛完成了這個算法,也許它可以幫助其他人尋求解決同樣的問題: http//jsfiddle.net/ akerbeltz / 9dQcn /

它的一個好處是它不需要對原始對象進行任何特殊排序。

如果您需要根據需要進行調整,請更改以下行:

  1. 根據您的結構更改_id和parentAreaRef.id。

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

  2. 根據您的結構更改parentAreaRef。

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

希望能幫助到你!

UPDATE

根據@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);

謝謝!

我知道我已經太晚了,但是因為我剛剛完成了對如何實現這一目標的示例實施的貢獻,我想我會分享它,因為它可能被認為是有用的/或者給予替代解決方案靈感。

可以在這里找到實現: http//jsfiddle.net/sw_lasse/9wpHa/

實現的主要思想圍繞以下遞歸函數:

// 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;
};

...用於構建樹。

您可以使用npm中的array-to-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; } } 

試試看:

   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);
           }
       }
   }

您的字符串中有錯誤

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

它應該是

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

我也很少優化它:

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