You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

反转嵌套树并合并节点:数组型节点树结构转换求助

问题描述

我有如下数据结构:

[
    {
        "id": 3,
        "name": "Important Topic 3",
        "questions": [array of questions],
        "topics": [array of topics],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic": null
                 }
            }
     },
    {
        "id": 4,
        "name": "Important Topic 4",
        "questions": [array of questions],
        "topics": [array of topics],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic": null
                 }
            }
     }
]

希望转换为如下嵌套树结构:

[
    {
        "id": 1,
        "name": "Parent Topic 2",
        "topics": [
            {
                "id": 2,
                "name": "Parent Topic 1",
                "topics": [
                    {
                      "id": 3,
                      "name": "Important Topic 3",
                      "questions": [array of questions],
                      "topics": [array of topics]
                    },
                    {
                      "id": 4,
                      "name": "Important Topic 4",
                      "questions": [array of questions],
                      "topics": [array of topics]
                    }
                ]
            }
        ]
    }
]

我找到的代码仅适用于单个嵌套字典对象,无法处理数组形式的字典列表,需要解决这个转换问题。

解决方案

可以通过节点映射+父链追溯+层级挂载的方式实现数组转嵌套树,以下是具体实现:

function buildNestedTree(items) {
    const nodeMap = new Map();

    // 遍历所有项,处理节点及其完整父链
    items.forEach(item => {
        let currentNode = item;
        // 将当前节点存入映射表,保留原始的questions和topics字段
        if (!nodeMap.has(currentNode.id)) {
            nodeMap.set(currentNode.id, {
                id: currentNode.id,
                name: currentNode.name,
                questions: currentNode.questions,
                topics: []
            });
        }

        // 向上遍历父节点链,创建父节点并挂载层级关系
        let parent = currentNode.parentTopic;
        while (parent) {
            if (!nodeMap.has(parent.id)) {
                nodeMap.set(parent.id, {
                    id: parent.id,
                    name: parent.name,
                    topics: []
                });
            }
            // 将当前节点挂载到父节点的topics数组(避免重复添加)
            const parentNode = nodeMap.get(parent.id);
            const currentMappedNode = nodeMap.get(currentNode.id);
            if (!parentNode.topics.includes(currentMappedNode)) {
                parentNode.topics.push(currentMappedNode);
            }
            // 继续处理父节点的上级父节点
            currentNode = parent;
            parent = parent.parentTopic;
        }
    });

    // 收集所有无父节点的根节点
    const rootNodes = [];
    items.forEach(item => {
        // 找到当前节点的最顶层父节点
        let topParent = item.parentTopic;
        while (topParent?.parentTopic) {
            topParent = topParent.parentTopic;
        }
        // 根节点去重后加入结果
        if (topParent && !rootNodes.some(node => node.id === topParent.id)) {
            rootNodes.push(nodeMap.get(topParent.id));
        }
        // 兼容本身就是根节点的情况
        if (!item.parentTopic && !rootNodes.some(node => node.id === item.id)) {
            rootNodes.push(nodeMap.get(item.id));
        }
    });

    return rootNodes;
}

// 测试用例
const input = [
    {
        "id": 3,
        "name": "Important Topic 3",
        "questions": [],
        "topics": [],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic": null
                 }
            }
     },
    {
        "id": 4,
        "name": "Important Topic 4",
        "questions": [],
        "topics": [],
        "parentTopic": {
                "id": 2,
                "name": "Parent Topic 1",
                "parentTopic": {
                    "id": 1,
                    "name": "Parent Topic 2",
                    "parentTopic": null
                 }
            }
     }
];

console.log(JSON.stringify(buildNestedTree(input), null, 2));

核心逻辑说明

  1. 节点映射表:用Map存储所有节点(包括原始节点和父节点),保证O(1)的查找效率,避免重复创建节点。
  2. 父链追溯:遍历每个节点的parentTopic链,递归创建所有层级的父节点,并将当前节点挂载到直接父节点的topics数组中。
  3. 根节点收集:通过追溯每个节点的最顶层父节点,筛选出所有无父节点的根节点,作为最终树结构的入口。

内容的提问来源于stack exchange,提问作者JimminyCricket

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:39:30