如何将含parent属性的数组转换为含children属性的层级数组?
扁平数组转层级结构:问题分析与优化方案
需求背景
你需要把一个扁平的英雄数组 currentModel 转换成带有层级嵌套结构的 requiredModel,其中每个节点的 children 数组包含其直接子节点。
原数组与目标结构
原扁平数组:
var currentModel = [ {id: 'Iron Man (CPC Avengers)', label: 'Iron Man', unit: 'CPC Avengers', level: '2', parent: '', children: []}, {id: 'Thor (Avengers Core Asgard)', label: 'Thor', unit: 'Avengers Core Asgard', level: '3', parent: 'Iron Man', children: []}, {id: 'Loki (Avengers Asgard)', label: 'Loki', unit: 'Avengers Asgard', level: '4', parent: 'Thor', children: []}, {id: 'Hela (Avengers Asgard)', label: 'Hela', unit: 'Avengers Asgard', level: '4', parent: 'Thor', children: []}, {id: 'Groot (Guardians of Galaxy)', label: 'Groot', unit: 'Guardians of Galaxy', level: '5', parent: 'Loki', children: []}, {id: 'Captain America (Avengers Core Earth)', label: 'Captain America', unit: 'Avengers Core Earth', level: '3', parent: 'Iron Man', children: []}, {id: 'Black Widow (Avengers Earth)', label: 'Black Widow', unit: 'Avengers Earth', level: '4', parent: 'Captain America', children: []}, {id: 'Hulk (Avengers Earth)', label: 'Hulk', unit: 'Avengers Earth', level: '4', parent: 'Captain America', children: []} ];
目标层级结构:
var requiredModel = [ { id: 'Iron Man (CPC Avengers)', label: 'Iron Man', unit: 'CPC Avengers', level: '2', parent: '', children: [ { id: 'Thor (Avengers Core Asgard)', label: 'Thor', unit: 'Avengers Core Asgard', level: '3', parent: 'Iron Man', children: [ { id: 'Loki (Avengers Asgard)', label: 'Loki', unit: 'Avengers Asgard', level: '4', parent: 'Thor', children: [ { id: 'Groot (Guardians of Galaxy)', label: 'Groot', unit: 'Guardians of Galaxy', level: '5', parent: 'Loki', children: []} ] }, {id: 'Hela (Avengers Asgard)', label: 'Hela', unit: 'Avengers Asgard', level: '4', parent: 'Thor', children: []} ] }, { id: 'Captain America (Avengers Core Earth)', label: 'Captain America', unit: 'Avengers Core Earth', level: '3', parent: 'Iron Man', children: [ { id: 'Black Widow (Avengers Earth)', label: 'Black Widow', unit: 'Avengers Earth', level: '4', parent: 'Captain America', children:[]}, { id: 'Hulk (Avengers Earth)', label: 'Hulk', unit: 'Avengers Earth', level: '4', parent: 'Captain America', children: []} ] } ] } ];
你的尝试与问题
你尝试先按 level 拆分不同层级的数组,再通过嵌套循环将子节点添加到父节点的 children 中,但运行时浏览器崩溃了。问题出在这段循环代码里:
for (var i = 0; i < currentModelLvl4.length; i++) { var currentModelItem4 = currentModelLvl4[i]; for (var j = 0; j < currentModelLvl5.length; j++) { // 这里写错了!应该是 currentModelLvl5[j],不是 currentModelLvl4[j] var currentModelItem5 = currentModelLvl4[j]; if (currentModelItem4.label === currentModelItem5.parent) { currentModelItem4.children.push(currentModelItem5); } } }
核心问题分析
- 变量赋值错误:
currentModelItem5被错误赋值为currentModelLvl4[j],导致循环逻辑完全混乱,甚至可能出现无限循环(如果数组长度变化的话),直接引发浏览器崩溃。 - 效率低下:即使修正了变量,嵌套循环的时间复杂度是 O(n²),当数组数据量较大时,性能会急剧下降,同样可能导致浏览器卡顿或崩溃。
- 层级依赖限制:按
level拆分的方式不够灵活,如果后续新增了level:6的节点,你需要手动添加新的拆分和循环逻辑,扩展性差。
更优的实现方案
我们可以用**哈希映射(对象)**来快速定位父节点,只需要遍历数组两次(甚至一次)就能完成转换,时间复杂度降到 O(n),同时逻辑更灵活,支持任意层级的嵌套。
实现代码
function buildHierarchicalModel(flatArray) { // 1. 创建映射表:用节点的 label 作为键,存储对应节点的引用 const nodeMap = {}; const rootNodes = []; // 2. 第一次遍历:填充映射表,并收集根节点(parent为空的节点) flatArray.forEach(node => { // 确保 children 数组存在(避免原数组中 children 未初始化的情况) node.children = node.children || []; nodeMap[node.label] = node; if (node.parent === '') { rootNodes.push(node); } }); // 3. 第二次遍历:将每个节点添加到其父节点的 children 数组中 flatArray.forEach(node => { const parentNode = nodeMap[node.parent]; // 如果父节点存在且当前节点不是根节点 if (parentNode && node.parent !== '') { parentNode.children.push(node); } }); return rootNodes; } // 使用示例 var requiredModel = buildHierarchicalModel(currentModel); console.log(requiredModel);
方案优势
- 高效:仅需两次遍历,时间复杂度 O(n),数据量大时性能优势明显。
- 灵活:无需关心层级数量,新增任意 level 的节点都能自动处理。
- 健壮:自动处理
children未初始化的情况,避免报错。
额外说明
如果你的 parent 字段对应的是节点的 id 而不是 label,只需要把映射表的键从 node.label 改成 node.id,同时判断条件里的 node.parent 对应查找 nodeMap[node.parent] 即可,逻辑完全通用。
内容的提问来源于stack exchange,提问作者loki
相关产品推荐
相关产品推荐

