学会这两种对象数组转树形结构,碰见它,“妈妈”再也不用担心我不会对象数组转树形结构了
很多小伙伴面试或工作中都会遇见数组对象转树形结构的问题,下面我们用两种方法来解决
示例对象数组
const items = [
{ id: 1, name: "item 1", parentId: null },
{ id: 8, name: "item 8", parentId: 5 },
{ id: 2, name: "item 2", parentId: 1 },
{ id: 3, name: "item 3", parentId: 1 },
{ id: 4, name: "item 4", parentId: null },
{ id: 5, name: "item 5", parentId: 4 },
{ id: 6, name: "item 6", parentId: 5 },
{ id: 7, name: "item 7", parentId: 6 },
{ id: 9, name: "item 9", parentId: null },
];
-
递归
递归方法通常直接在函数内部调用自身来处理子节点,直到达到某个终止条件(通常是遇到没有子节点的节点)。递归方法的清晰在于逻辑表达直接,但缺点是当数据规模非常大时,可能会导致调用栈溢出。
function listToTree(list, parentId = null) { let tree = []; // 遍历数组 for (let i = 0; i < list.length; i++) { // 如果当前节点的parentId等于传入的parentId,则将当前节点添加到tree中 if (list[i].parentId === parentId) { // 递归调用listToTree函数 list[i].children = listToTree(list, list[i].id); tree.push(list[i]); } } return tree; }
-
直接遍历并使用Map
使用Map(或对象字面量作为映射表)的主要优势在于能够提前将所有节点存储起来,通过键值对形式快速访问,从而避免了在递归或其他遍历过程中重复查找元素,大大提升了效率。这种方法特别适用于处理大规模数据集。
function listToTree(list) { const result = []; // 将数组中的对象转换为节点,并将节点保存到map中 const map = new Map( list.map((item) => [item.id, { ...item, children: [] }]) ); // 遍历数组中的每一个对象,将其添加到对应的父节点的子节点数组中 for (const item of list) { // 去集合中找对应的子节点 const node = map.get(item.id); if (item.parentId === null) { // 如果parentId不存在,则将节点添加到根节点数组中 result.push(node); } else { // 去集合中找对应的父节点 const parent = map.get(item.parentId); // 如果父节点存在,把子节点放入到父节点的children数组中 if (parent) { parent.children.push(node); } } } return result; }
我们测试一下,让我们在控制台打印一下,打印结果如下
[
{
"id": 1,
"name": "item 1",
"parentId": null,
"children": [
{
"id": 2,
"name": "item 2",
"parentId": 1,
"children": []
},
{
"id": 3,
"name": "item 3",
"parentId": 1,
"children": []
}
]
},
{
"id": 4,
"name": "item 4",
"parentId": null,
"children": [
{
"id": 5,
"name": "item 5",
"parentId": 4,
"children": [
{
"id": 8,
"name": "item 8",
"parentId": 5,
"children": []
},
{
"id": 6,
"name": "item 6",
"parentId": 5,
"children": [
{
"id": 7,
"name": "item 7",
"parentId": 6,
"children": []
}
]
}
]
}
]
},
{
"id": 9,
"name": "item 9",
"parentId": null,
"children": []
}
]
快试试吧!
区别总结:
递归方法更直观地展示了树的构建过程,但可能不适合处理极深或极大的树结构,因为递归深度受限于调用栈的大小。
Map方法通过预处理提高查找效率,尤其适合大数据集,因为它减少了查找时间,但代码逻辑相比递归可能稍显复杂,需要额外处理映射表。