如何将扁平数组高效构建为嵌套树形结构

梦婷姑娘_5869

梦婷姑娘_5869

2026-03-30

820人浏览

原创

如何将扁平数组高效构建为嵌套树形结构

本文详解如何使用 JavaScript 的 reduce 与递归结合,将含 id/parentId 关系的扁平对象数组(如组织架构、设备层级等)转换为标准嵌套树结构,支持任意深度,并避免常见误区(如仅按类型分组而非真实父子关系)。

本文详解如何使用 javascript 的 reduce 与递归结合,将含 `id`/`parentid` 关系的扁平对象数组(如组织架构、设备层级等)转换为标准嵌套树结构,支持任意深度,并避免常见误区(如仅按类型分组而非真实父子关系)。

在实际开发中(如设备管理、菜单配置、组织架构渲染),我们常遇到一类数据:它以扁平数组形式存储具有父子关系的节点,每个对象包含唯一标识 id 和指向父节点的 parentId。目标是将其转化为符合 DOM 渲染或组件库(如 Ant Design Tree、Element Plus ElTree)要求的标准树形结构——即每个节点通过 children 数组持有其直接子节点,且根节点 parentId 通常为 "0" 或 null。

你之前使用的 reduce 方法本质是按 type 字段做分类聚合,生成的是横向分组结构(如 { Building: [...], Room: [...] }),而非按 parentId → id 构建的纵向层级结构。正确解法需抓住两个核心逻辑:

  • ✅ 定位根节点:筛选出所有 parentId === "0"(或指定根 ID)的项作为顶层入口;
  • ✅ 递归挂载子树:对每个节点,再次遍历原数组,找出所有 parentId 等于当前节点 id 的子项,并递归构建其 children。

以下是推荐的简洁、健壮、可读性强的实现方案:

/**
 * 将扁平数组转换为树形结构
 * @param {Array} data - 源数据数组,每个对象需含 id 和 parentId 字段
 * @param {string|number|null} [parentId="0"] - 根节点的 parentId 值(默认 "0")
 * @returns {Array} 树形结构数组(森林,可能含多个根)
 */
function toTree(data, parentId = "0") {
  return data.reduce((acc, node) => {
    // 当前节点是目标 parentId 的子节点时,纳入结果
    if (node.parentId == parentId) {
      // 浅拷贝节点,避免污染原始数据
      const newNode = { ...node };
      // 递归构建 children 数组
      newNode.children = toTree(data, node.id);
      acc.push(newNode);
    }
    return acc;
  }, []);
}

// 使用示例
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: "9", name: "Rack_1", parentId: "5", type: "Rack" },
  { id: "12", name: "Shelf_01", parentId: "9", type: "Shelf" },
  { id: "13", name: "Slot_1", parentId: "12", type: "Slot" }
];

const tree = toTree(arrData);
console.log(JSON.stringify(tree, null, 2));

✅ 关键优势与注意事项

  • 时间复杂度可控:虽为递归 + 双重遍历(O(n²) 最坏情况),但对千级以内节点性能无压力;如需极致性能(万级+),建议先用 Map 预建 id → nodes[] 索引,将查找优化至 O(1);
  • 健壮性设计:使用 == 而非 === 兼容字符串/数字型 ID(如 parentId: 0 与 "0");若需严格类型匹配,可改为 node.parentId === String(parentId);
  • 空 children 处理:当某节点无子节点时,toTree(data, nodeId) 返回空数组 [],符合规范,无需额外过滤;
  • 避免副作用:始终返回新对象({...node}),不修改原始数组,保障函数纯度;
  • 支持多根树:若数据含多个 parentId === "0" 节点(如多个独立建筑),结果将为森林(数组含多个根节点),符合真实业务场景。

? 进阶建议

  • 若需支持动态加载(懒加载子节点),可将 children 替换为 isLeaf: boolean + 异步 loadChildren() 方法;
  • 结合 TypeScript 可定义泛型接口,提升类型安全:
    interface TreeNode<t any> {
      id: string | number;
      parentId: string | number | null;
      children?: TreeNode<t>[];
      [key: string]: T | string | number | null | TreeNode<t>[] | undefined;
    }
    function toTree<t extends treenode>(data: T[], parentId: string | number = "0"): T[] { ... }</t></t></t></t>

掌握这一模式,你便能灵活应对各类层级化数据建模需求——从机房设备拓扑到企业组织图谱,皆可一招制胜。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

5066

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1249

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4984

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

980

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1896

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3565

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4633

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

3040

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

968

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.9万人学习