
本文详解如何基于 parentid/id 关系,将一维对象数组高效构建为深度嵌套的树形结构,支持任意层级,并提供可直接运行的递归实现与关键注意事项。
本文详解如何基于 parentid/id 关系,将一维对象数组高效构建为深度嵌套的树形结构,支持任意层级,并提供可直接运行的递归实现与关键注意事项。
在前端开发与数据可视化场景中,常需将后端返回的扁平化层级数据(如组织架构、设备拓扑、目录列表)转换为符合 DOM 渲染或组件库(如 Ant Design Tree、Element Plus ElTree)要求的嵌套树结构。核心挑战在于:正确识别父子关系、避免重复遍历、保证子节点按逻辑顺序挂载,且不破坏原始数据字段完整性。
你当前使用的 reduce 按 type 分组的方式,本质上是横向分类(分组聚合),而非纵向层级构建——它丢失了 id 与 parentId 的引用链,自然无法生成嵌套的 children 数组。真正可行的方案是:以根节点为起点,递归查找所有 parentId 匹配当前节点 id 的子项,并为每个子项继续递归构造其子树。
以下是一个简洁、健壮、无副作用的实现:
function buildTree(data, rootId = '0') {
// 使用 reduce 遍历全量数据,筛选出 parentId === 当前 rootId 的节点
return data.reduce((tree, node) => {
if (node.parentId === rootId) {
// 浅拷贝当前节点,避免污染原始数据
const newNode = { ...node };
// 递归构建其子树,并赋值给 children(注意:字段名统一为 children,非 childeren 拼写错误)
newNode.children = buildTree(data, node.id);
tree.push(newNode);
}
return tree;
}, []);
}
// 使用示例
const arrData = [
{ id: "3", name: "Ctech A", parentId: "0", type: "Building" },
{ id: "4", name: "3rd floor", parentId: "3", type: "Floor" },
{ id: "5", name: "room_01", parentId: "4", type: "Room" },
{ id: "6", name: "room_video", parentId: "4", type: "Room" },
{ id: "7", name: "room_Lab", parentId: "4", type: "Room" },
{ id: "8", name: "room_engg", parentId: "4", type: "Room" },
{ id: "9", name: "Rack_1", parentId: "5", type: "Rack" },
{ id: "10", name: "Rack_2", parentId: "5", type: "Rack" },
{ id: "11", name: "Rack_3", parentId: "5", type: "Rack" },
{ id: "12", name: "Shelf_01", parentId: "9", type: "Shelf" },
{ id: "13", name: "Slot_1", parentId: "12", type: "Slot" },
{ id: "14", name: "Slot_2", parentId: "12", type: "Slot" },
{ id: "15", name: "Shelf_02", parentId: "9", type: "Shelf" }
];
const tree = buildTree(arrData);
console.log(JSON.stringify(tree, null, 2));
✅ 关键要点说明:
- 根节点判定:rootId 默认设为 '0'(字符串),需与数据中 parentId: "0" 类型一致;若后端使用数字 0,请同步调整为 rootId = 0 并用 === 严格比较。
- 字段命名一致性:输出中统一使用 children(标准英文复数),请确保你的消费方(如 UI 组件)也期望该字段名;原问题中误写为 childeren,属典型拼写错误,务必修正。
- 时间复杂度:该算法为 O(n²) —— 每层递归均遍历全量数组。对万级数据建议优化为 哈希索引预处理(见下方进阶方案)。
- 空子树处理:当某节点无子节点时,buildTree(data, node.id) 返回空数组 [],符合 JSON 树规范,无需额外过滤。
? 性能进阶(适用于大数据量):
若数据量超过 1000 条,推荐先建立 id → 节点 的 Map 索引,将时间复杂度降至 O(n):
function buildTreeOptimized(data, rootId = '0') {
const map = new Map(data.map(node => [node.id, { ...node, children: [] }]));
const roots = [];
for (const node of data) {
if (node.parentId === rootId) {
roots.push(map.get(node.id));
} else {
const parent = map.get(node.parentId);
if (parent) parent.children.push(map.get(node.id));
}
}
return roots;
}
? 总结: 构建树形结构的本质是图的深度优先遍历(DFS)。切勿混淆“按类型分组”与“按父子关系嵌套”两种范式。始终以 id/parentId 为唯一依据,采用递归或迭代+栈/队列方式,辅以合适的数据结构(如 Map)提升效率。代码应保持纯函数特性,避免修改原始输入,确保可预测性与可维护性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











