JavaScript 对象属性分组构建嵌套树结构的完整实现教程

1次阅读

JavaScript 对象属性分组构建嵌套树结构的完整实现教程

本文介绍如何将扁平对象数组按指定属性顺序(如 [‘a’, ‘b’, ‘c’, ‘d’])递归分组,生成符合图表库要求的嵌套树形结构(含 name 和 children 字段),支持多层级动态聚合与去重。

数据可视化场景中(如 echarts、D3 或自定义层级图),常需将扁平的原始数据转换为具有明确父子关系的嵌套结构。典型需求是:给定一组具有相同键名(如 A、B、C、D)的对象,按属性顺序逐层分组,最终形成 name + children 的标准树节点格式。本教程提供一种简洁、健壮且可扩展的实现方案。

核心思路:两阶段构造法

我们采用「先建模、后转换」的两阶段策略:

  1. 构建嵌套哈希树:利用 javaScript 的 ??=(空值合并赋值)操作符,按属性顺序逐层创建嵌套对象,自动处理路径分支与重复键;
  2. 递归展平为树节点:通过 Object.entries() + 递归映射,将嵌套对象结构转化为目标格式(无子节点时省略 children 数组)。

完整实现代码

/**  * 将对象数组按指定属性顺序分组,生成嵌套树结构  * @param {Array} data - 输入的扁平对象数组  * @param {Array} keys - 分组属性名顺序,如 ['A', 'B', 'C', 'D']  * @returns {Object} 符合 name/children 规范的根节点  */ function groupByProperties(data, keys) {   if (!Array.isArray(data) || data.length === 0 || !Array.isArray(keys) || keys.length === 0) {     return {};   }    // 阶段一:构建嵌套对象树(以属性值为键)   const root = {};   for (const obj of data) {     let node = root;     for (const key of keys) {       const value = obj[key];       // 确保每层都存在对应子对象,自动创建缺失路径       node = (node[value] ??= {});     }   }    // 阶段二:递归转换为标准树节点结构   const buildNode = (obj) => {     return Object.entries(obj).map(([name, child]) => {       // 若 child 为空对象(即叶子节点),只返回 { name }       // 否则递归构建 children       const children = Object.keys(child).length > 0          ? buildnode(child)          : undefined;        return children          ? { name: number(name), children }          : { name: Number(name) };     });   };    // 返回根节点(因 keys 非空,buildNode(root) 至少返回一个元素)   return buildNode(root)[0] || {}; }  // ✅ 示例 1:同 A/B/C,D 不同 → 单分支多叶 const data1 = [   { A: 1, B: 5, C: 7, D: 67 },   { A: 1, B: 5, C: 7, D: 69 } ]; console.log(groupByProperties(data1, ['A', 'B', 'C', 'D'])); // 输出: // { name: 1, children: [{ name: 5, children: [{ name: 7, children: [{ name: 67 }, { name: 69 }] }] }] }  // ✅ 示例 2:C 层出现分支 → 多子节点并列 const data2 = [   { A: 1, B: 5, C: 4, D: 67 },   { A: 1, B: 5, C: 7, D: 69 } ]; console.log(groupByProperties(data2, ['A', 'B', 'C', 'D'])); // 输出: // { name: 1, children: [{ name: 5, children: [{ name: 4, children: [{ name: 67 }] }, { name: 7, children: [{ name: 69 }] }] }] }

关键特性与注意事项

  • 自动类型转换:示例中使用 Number(name) 统一转为数值类型(适配多数图表库)。若需保留字符串(如 ‘A1’),可移除 Number() 包裹;
  • 空值安全:使用 ??= 避免 undefined 访问错误,天然支持稀疏或缺失属性(但需确保输入对象中所有 keys 对应字段均存在);
  • 顺序严格性:keys 数组顺序决定分组层级,不可颠倒(如 [‘B’, ‘A’] 将产生完全不同的树结构);
  • 性能优化:时间复杂度为 O(n × k)(n 为数据量,k 为 keys 长度),空间复杂度取决于唯一路径数,适用于万级以内数据;
  • 扩展建议:如需支持自定义节点字段(如添加 id、label)、过滤条件或排序逻辑,可在 buildNode 内部增强映射逻辑。

该方案轻量、无外部依赖,可直接集成至前端数据预处理流程,高效支撑各类层级可视化需求。

Copyright ©  SEO

 Theme by Puock