
本文介绍如何高效地将数据库返回的扁平化数组(如多级分类数据)转换为指定的深度嵌套 json 结构,通过哈希缓存 + 一次遍历实现 o(n) 时间复杂度,避免重复遍历与深层嵌套判断。
本文介绍如何高效地将数据库返回的扁平化数组(如多级分类数据)转换为指定的深度嵌套 json 结构,通过哈希缓存 + 一次遍历实现 o(n) 时间复杂度,避免重复遍历与深层嵌套判断。
在实际开发中,后端常以扁平化格式(如宽表或展平的关联查询结果)返回层级数据——例如汽车品牌(manufacture)、车系(make)、车型(model)三者共存于同一层对象中。但前端 UI(如级联选择器、树形菜单)往往需要严格嵌套结构:
[
{
"manufacture": "Ford",
"makes": [
{
"make": "Mustang",
"models": [
{ "model": "EcoBoost" },
{ "model": "GTFastback" }
]
}
]
}
]
手动逐层 find 或 push 不仅冗长易错,且时间复杂度达 O(n²),尤其在数据量增大时性能急剧下降。更优解是利用 JavaScript 对象作哈希缓存(Hash Cache),在单次遍历中完成三级结构的动态构建。
✅ 核心思路:键驱动的“获取或创建”模式
我们为每一级节点设计唯一缓存键:
- 品牌级:manufacture(如 "Ford")
- 车系级:manufacture + ":" + make(如 "Ford:Mustang")
- 节点统一挂载到共享缓存对象 g 下,根数组存于 g._
✅ 实现代码(简洁健壮版)
const data = [
{ manufacture: 'Ford', make: 'Mustang', model: 'EcoBoost' },
{ manufacture: 'Ford', make: 'Mustang', model: 'GTFastback' },
{ manufacture: 'Ford', make: 'Escape', model: 'Active' },
{ manufacture: 'Toyota', make: 'Camry', model: 'XSE' },
{ manufacture: 'Toyota', make: 'Supra', model: '3.0' }
];
const g = { _: [] }; // 缓存容器,_ 存最终结果数组
for (const { manufacture, make, model } of data) {
// 获取或创建 manufacture 级节点
const brandNode = g[manufacture] ??= { manufacture, makes: [] };
if (!g._.includes(brandNode)) g._.push(brandNode);
// 获取或创建 make 级节点
const key = `${manufacture}:${make}`;
const seriesNode = g[key] ??= { make, models: [] };
if (!brandNode.makes.includes(seriesNode)) brandNode.makes.push(seriesNode);
// 追加 model
seriesNode.models.push({ model });
}
console.log(g._); // 输出符合目标结构的嵌套数组
? 关键技巧说明:
- ??=(空值合并赋值)确保只在键不存在时初始化,避免覆盖;
- if (!arr.includes(node)) 防止同一节点被重复加入(适用于非严格有序输入);
- 所有操作均为引用传递,无需深拷贝,内存与性能兼优。
⚠️ 注意事项
- 键唯一性保障:若 manufacture 或 make 本身含 :,需改用更安全分隔符(如 \u0000)或 JSON.stringify([manufacture, make]) 生成键;
- 字段容错:生产环境建议增加空值校验(如 !manufacture || !make || !model)并跳过脏数据;
- 扩展性:该模式可轻松扩展至四层(如增加 trim 属性),只需新增一级缓存键与节点结构即可。
此方法兼顾可读性、性能与可维护性,是处理层级聚合类数据转换的推荐实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











