
本文介绍如何将 javascript 对象数组依据给定属性顺序(如 `['a', 'b', 'c', 'd']`)递归分组,生成符合图表库要求的嵌套树形结构(每个节点含 `name` 与可选 `children` 数组)。
在数据可视化场景中(如 ECharts、D3 或自定义层级图),常需将扁平的原始数据转换为深度嵌套的树状结构。本教程提供一种健壮、可复用的解决方案:根据用户指定的属性键序列(如 ['A', 'B', 'C', 'D']),对对象数组进行多级分组,并输出标准的 name + children 树形格式。
核心思路分为两步:
- 构建嵌套哈希表:遍历每条数据,按 keys 顺序逐层下钻,用对象字面量模拟“路径”,最终形成类似 { "1": { "5": { "7": { "67": {}, "69": {} } } } } 的中间结构;
- 递归转为目标树形:通过 Object.entries() 和递归映射,将嵌套对象自动展开为带 name 和 children 的数组结构——叶子节点无 children,非叶子节点则递归处理子对象。
以下是完整实现代码(已优化可读性与健壮性):
function groupByKeys(data, keys) {
if (!Array.isArray(data) || data.length === 0 || !Array.isArray(keys) || keys.length === 0) {
return {};
}
// Step 1: 构建嵌套对象树(使用空对象作为叶子占位符)
const root = {};
for (const item of data) {
let node = root;
for (const key of keys) {
const value = item[key];
// 使用 ??= 确保深层嵌套对象被安全创建
node = (node[value] ??= {});
}
}
// Step 2: 递归转换为 name/children 格式
const buildTree = (obj) => {
return Object.entries(obj).map(([name, child]) => {
// 若 child 为空对象(即无子属性),视为叶子节点
if (Object.keys(child).length === 0) {
return { name: Number(name) || name }; // 自动转数字(可选)
}
// 否则递归构建 children
return {
name: Number(name) || name,
children: buildTree(child)
};
});
};
const result = buildTree(root);
return result.length > 0 ? result[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(groupByKeys(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(groupByKeys(data2, ['A', 'B', 'C', 'D']));
// 输出:{ name: 1, children: [{ name: 5, children: [{ name: 4, children: [{ name: 67 }] }, { name: 7, children: [{ name: 69 }] }] }] }⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
- 键名一致性:确保 keys 中每个属性在所有对象中均存在,否则会因 item[key] 为 undefined 导致意外分组(建议预校验或使用默认值);
- 类型处理:示例中 Number(name) || name 尝试将纯数字字符串转为数值,如需严格保留原始类型(如字符串 '01'),请移除该转换;
- 性能考量:该算法时间复杂度为 O(n × k)(n 为数据量,k 为 keys 长度),适用于万级以内数据;超大规模时可考虑 Map 替代普通对象提升查找效率;
- 空值/重复处理:当前逻辑不自动去重或过滤 null/undefined,如需支持,可在 for (const item of data) 循环内添加预处理逻辑。
此方案简洁、无外部依赖,可直接集成至任意前端项目,轻松满足各类层级图表的数据准备需求。










