使用id作为引用以递归方式将对象数组嵌套到树中的最佳方法是什么?

时间:2017-11-07 00:42:18

标签: javascript json recursion

我试图创建一个"树"查看我的数据库中的所有页面。要做到这一点,我已经给每一页"父母"属性以及它所属的页面的唯一ID。

现在我想以递归方式将这个对象数组嵌套在自身内部,以便所有子页面都嵌套在相应的父页面中。使用父标识作为参考,从以下方法转换以下数组的最有效方法是什么:

[ 
    { 
        _id: 1,
        title: 'Page A-1',
        parent: null 
    },
    { 
        _id: 2,
        title: 'Page A-2',
        parent: 1 
    },
    { 
        _id: 3,
        title: 'Page A-3',
        parent: 2 
    },
    { 
        _id: 4,
        title: 'Page A-4',
        parent: 3 
    },
    { 
        _id: 5,
        title: 'Page A-5',
        parent: 4 
    },
    { 
        _id: 6,
        title: 'Page B-1',
        parent: null 
    },
    { 
        _id: 7,
        title: 'Page B-2',
        parent: 6 
    },
    { 
        _id: 8,
        title: 'Page B-3',
        parent: 7 
    },
    { 
        _id: 9,
        title: 'Page B-4',
        parent: 8 
    },
    { 
        _id: 10,
        title: 'Page B-5',
        parent: 8 
    },
    { 
        _id: 11,
        title: 'Page C-1',
        parent: null
    }
]

进入这个:

[ 
    { 
        _id: 1,
        title: 'Page A-1',
        children: [
            { 
                _id: 2,
                title: 'Page A-2',
                children: [
                    { 
                        _id: 3,
                        title: 'Page A-3',
                        children: [
                            { 
                                _id: 4,
                                title: 'Page A-4',
                                children: [
                                    { 
                                        _id: 5,
                                        title: 'Page A-5',
                                        children: [

                                        ]
                                    }       
                                ]
                            }       
                        ]
                    }
                ]
            }       
        ]
    },
    { 
        _id: 6,
        title: 'Page B-1',
        children: [
            { 
                _id: 7,
                title: 'Page B-2',
                children: [
                    { 
                        _id: 8,
                        title: 'Page B-3',
                        children: [
                            { 
                                _id: 9,
                                title: 'Page B-4',
                                children: []
                            },
                            { 
                                _id: 10,
                                title: 'Page B-5',
                                children: []
                            }
                        ]
                    }       
                ]
            }
        ]
    },
    { 
        _id: 11,
        title: 'Page C-1',
        children: []
    }
]

2 个答案:

答案 0 :(得分:1)

在循环遍历数组时,我会查找所有对象,并在同一循环中将子项添加到父项。假设您的数据不是很大而且存在内存问题,您可以在一个循环中通过数组执行此操作,而无需在父树中搜索树。

[注意:这假设父项是在数组中的子项之前定义的。如果不是你,你将不得不在循环中做更多的工作(但不多)]



let data = [
    {
        _id: 1,
        title: 'Page A-1',
        parent: null
    },
    {
        _id: 2,
        title: 'Page A-2',
        parent: 1
    },
    {
        _id: 3,
        title: 'Page A-3',
        parent: 2
    },

    {
        _id: 4,
        title: 'Page A-4',
        parent: 3
    },
    {
        _id: 5,
        title: 'Page A-5',
        parent: 4
    },
    {
        _id: 6,
        title: 'Page B-1',
        parent: null
    },
    {
        _id: 7,
        title: 'Page B-2',
        parent: 6
    },
    {
        _id: 8,
        title: 'Page B-3',
        parent: 7
    },
    {
        _id: 9,
        title: 'Page B-4',
        parent: 8
    },
    {
        _id: 10,
        title: 'Page B-5',
        parent: 8
    },
    {
        _id: 11,
        title: 'Page C-1',
        parent: null
    }
]

let lookup = {}
let tree = {}

data.forEach(item => {
    let obj = {_id: item._id, title: item.title, children: [] }
    // add to lookup
    if(!lookup[item._id]) lookup[item._id] =  obj

    if(!item.parent) {
        // a root node. Add directly to tree
        tree[item._id] = obj
    } else{
        //  a child node. Add to parent's children array
        lookup[item.parent].children.push(obj)
    }
})
console.log(tree)




答案 1 :(得分:1)

我的回答将忽略这样做的必要性,并在大O复杂性方面为您提供最强大的时间效率方法。

算法包含线性复杂度O(items.length)的三个步骤,这使得整个算法也是线性的。

注意:由于常见的权衡,我牺牲空间来实现最佳时间复杂度。

步骤1):

遍历您的项目并构建Map<id, object>

// you can also use a POJO here
// i'm using a map to demonstrate the appropriate data structure
const map = items.reduce((map, item) => {
  map.set(item._id, item);
  // Note: it might be safer to not use original objects when building the tree
  // and create new objects instead. See the comment in step 2 as well.
  /*
    map.set(item._id, {
      ...item,
      children: []
    })
  */
  return map;
}, new Map())

步骤2)

遍历您的项目并将每个项目嵌套在其父项下。

items.forEach(item => {
  if (item.parent) {
    const parent = map.get(item.parent);
    // Note: this will actually edit your original objects
    // Since mutability is the cause of many bugs, if you can afford it
    // I'd create new objects with the children property in step 1
    // when inserting items into the map which would make this if
    // statement redundant
    if (!parent.children) {
      parent.children = [];
    }
    parent.children.push(item);
  }
})

步骤3)

迭代地图中的项目并选择顶级父母:

const result = [];
for (const item of map.values()) {
  if (!item.parent) {
    result.push(item);
  }
}

以下是一个完整的例子:

&#13;
&#13;
const items=[{_id:1,title:"Page A-1",parent:null},{_id:2,title:"Page A-2",parent:1},{_id:3,title:"Page A-3",parent:2},{_id:4,title:"Page A-4",parent:3},{_id:5,title:"Page A-5",parent:4},{_id:6,title:"Page B-1",parent:null},{_id:7,title:"Page B-2",parent:6},{_id:8,title:"Page B-3",parent:7},{_id:9,title:"Page B-4",parent:8},{_id:10,title:"Page B-5",parent:8},{_id:11,title:"Page C-1",parent:null}];

// step 1
const map = items.reduce((map, item) => {
  map.set(item._id, item);
  return map;
}, new Map())

// step 2
items.forEach(item => {
  if (item.parent) {
    const parent = map.get(item.parent);
    if (!parent.children) {
      parent.children = [];
    }
    parent.children.push(item);
  }
})

// step 3
const result = [];
for (const item of map.values()) {
  if (!item.parent) {
    result.push(item);
  }
}

console.log(result);
&#13;
&#13;
&#13;

在现实世界中,性能不仅取决于算法的复杂性,还取决于所使用的所有操作和数据结构的实际实现细节。如果这种关键性能对您很重要,那么构建最佳算法需要进行实际的基准测试。