两种对象数组转树形结构

80 阅读3分钟

学会这两种对象数组转树形结构,碰见它,“妈妈”再也不用担心我不会对象数组转树形结构了

很多小伙伴面试或工作中都会遇见数组对象转树形结构的问题,下面我们用两种方法来解决

示例对象数组

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 },
];

  1. 递归

    递归方法通常直接在函数内部调用自身来处理子节点,直到达到某个终止条件(通常是遇到没有子节点的节点)。递归方法的清晰在于逻辑表达直接,但缺点是当数据规模非常大时,可能会导致调用栈溢出。

    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;
    }
    
  2. 直接遍历并使用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方法通过预处理提高查找效率,尤其适合大数据集,因为它减少了查找时间,但代码逻辑相比递归可能稍显复杂,需要额外处理映射表。