如何用JavaScript将基于source-target关联的扁平JSON转为树形结构
把扁平JSON转成基于links关联的树形视图
嘿,我来帮你搞定这个需求!你手头的JSON是典型的节点-边结构,要转成树形视图的话,核心就是通过links里的source和target索引,把节点之间的父子关系建立起来。下面我用JavaScript给你写个实用的实现,思路清晰还容易扩展~
先理清楚核心逻辑
- 建立节点映射表:把所有节点按
index存成键值对,方便快速查找,同时给每个节点加上children数组用来存子节点。 - 关联父子关系:遍历每一条
link,找到父节点(source对应的节点),把子节点(target对应的节点)加到父节点的children里。 - 收集根节点:根节点就是那些从来没被任何
link的target指向的节点——毕竟树的顶层节点没有父节点嘛。
完整代码实现
首先先把你给的原始JSON修正成合法格式(注意属性名要加双引号):
{ "nodes": [ {"name": "bz_db", "index": 0}, {"name": "mysql", "index": 1}, {"name": "postgres", "index": 2}, {"name": "it-infra", "index": 3}, {"name": "user-count", "index": 4} ], "links": [ {"source": 0, "target": 1}, {"source": 0, "target": 3}, {"source": 1, "target": 3}, {"source": 3, "target": 4} ] }
然后是转换函数:
function flatToTree(data) { // 第一步:创建节点映射,给每个节点初始化children数组 const nodeMap = {}; data.nodes.forEach(node => { nodeMap[node.index] = { ...node, children: [] }; }); // 第二步:遍历links,建立父子关联 data.links.forEach(link => { const parent = nodeMap[link.source]; const child = nodeMap[link.target]; parent.children.push(child); }); // 第三步:找出所有根节点(没有被作为target的节点) const allTargetIndexes = new Set(data.links.map(link => link.target)); const treeRoots = data.nodes .filter(node => !allTargetIndexes.has(node.index)) .map(node => nodeMap[node.index]); return treeRoots; } // 测试一下 const inputData = { "nodes": [ {"name": "bz_db", "index": 0}, {"name": "mysql", "index": 1}, {"name": "postgres", "index": 2}, {"name": "it-infra", "index": 3}, {"name": "user-count", "index": 4} ], "links": [ {"source": 0, "target": 1}, {"source": 0, "target": 3}, {"source": 1, "target": 3}, {"source": 3, "target": 4} ] }; const treeResult = flatToTree(inputData); console.log(JSON.stringify(treeResult, null, 2));
输出结果
运行后你会得到这样的树形结构:
[ { "name": "bz_db", "index": 0, "children": [ { "name": "mysql", "index": 1, "children": [ { "name": "it-infra", "index": 3, "children": [ { "name": "user-count", "index": 4, "children": [] } ] } ] }, { "name": "it-infra", "index": 3, "children": [ { "name": "user-count", "index": 4, "children": [] } ] } ] }, { "name": "postgres", "index": 2, "children": [] } ]
小说明
这里你会看到it-infra出现了两次,因为原始的links里有两条指向它的关系(bz_db→it-infra和mysql→it-infra)。如果你的需求是每个节点只能在树里出现一次,那可以加个visited集合来过滤重复添加的节点——不过这个得看你具体的业务场景啦~
内容的提问来源于stack exchange,提问作者kaounKaoun
相关产品推荐
相关产品推荐

