反转嵌套树并合并节点:数组型节点树结构转换求助
问题描述
我有如下数据结构:
[ { "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));
核心逻辑说明
- 节点映射表:用
Map存储所有节点(包括原始节点和父节点),保证O(1)的查找效率,避免重复创建节点。 - 父链追溯:遍历每个节点的
parentTopic链,递归创建所有层级的父节点,并将当前节点挂载到直接父节点的topics数组中。 - 根节点收集:通过追溯每个节点的最顶层父节点,筛选出所有无父节点的根节点,作为最终树结构的入口。
内容的提问来源于stack exchange,提问作者JimminyCricket
相关产品推荐
相关产品推荐

