
本文介绍如何高效地将数据库返回的扁平化汽车数据(含 manufacture、make、model 三层字段)转换为符合 { manufacture, makes: [{ make, models: [{ model }] }] } 规范的嵌套 json 结构,避免多重嵌套循环,利用哈希缓存实现 o(n) 时间复杂度转换。
本文介绍如何高效地将数据库返回的扁平化汽车数据(含 manufacture、make、model 三层字段)转换为符合 { manufacture, makes: [{ make, models: [{ model }] }] } 规范的嵌套 json 结构,避免多重嵌套循环,利用哈希缓存实现 o(n) 时间复杂度转换。
在实际开发中,后端常以扁平化形式(如宽表)返回多层级关联数据,而前端 UI 组件(如级联选择器、树形列表)往往需要深度嵌套的结构。手动遍历 + 条件判断虽可行,但易出错、难以维护。下面提供一种简洁、高效、可扩展的 JavaScript 解决方案。
核心思路:单次遍历 + 哈希缓存(Map/Plain Object)
我们不使用 reduce 或嵌套 find,而是借助一个统一缓存对象 cache,按需创建并复用各层级节点:
- 第一层键为 manufacture → 对应厂商对象;
- 第二层键为 manufacture:make → 对应车型对象;
- 每个模型直接 push 到对应 models 数组。
这样仅需一次 for...of 遍历,时间复杂度 O(n),空间复杂度 O(k)(k 为唯一厂商+车型组合数)。
✅ 完整可运行示例代码
const rawData = [
{ manufacture: 'Ford', make: 'Mustang', model: 'EcoBoost' },
{ manufacture: 'Ford', make: 'Mustang', model: 'GTFastback' },
{ manufacture: 'Ford', make: 'Mustang', model: 'DarkHorse' },
{ manufacture: 'Ford', make: 'Escape', model: 'Active' },
{ manufacture: 'Ford', make: 'Escape', model: 'Platium' },
{ manufacture: 'Toyota', make: 'Camry', model: 'XSE' },
{ manufacture: 'Toyota', make: 'Camry', model: 'TRD' },
{ manufacture: 'Toyota', make: 'Camry', model: 'XLE' },
{ manufacture: 'Toyota', make: 'Supra', model: '2.0' },
{ manufacture: 'Toyota', make: 'Supra', model: '3.0' }
];
// 缓存对象:根数组存储最终结果,其余键用于快速查找节点
const cache = { _: [] };
for (const { manufacture, make, model } of rawData) {
// 获取或创建 manufacture 节点
const manuNode = cache[manufacture] ??= {
manufacture,
makes: []
};
if (!cache._.includes(manuNode)) cache._.push(manuNode);
// 获取或创建 make 节点(用 manufacturer:make 复合键避免命名冲突)
const makeKey = `${manufacture}:${make}`;
const makeNode = cache[makeKey] ??= {
make,
models: []
};
if (!manuNode.makes.includes(makeNode)) manuNode.makes.push(makeNode);
// 添加 model
makeNode.models.push({ model });
}
// 最终结果即 cache._
console.log(JSON.stringify(cache._, null, 2));
? 输出结构示例(节选)
[
{
"manufacture": "Ford",
"makes": [
{
"make": "Mustang",
"models": [
{ "model": "EcoBoost" },
{ "model": "GTFastback" },
{ "model": "DarkHorse" }
]
},
{
"make": "Escape",
"models": [
{ "model": "Active" },
{ "model": "Platium" }
]
}
]
},
{
"manufacture": "Toyota",
"makes": [
/* ... */
]
}
]
⚠️ 注意事项与优化建议
- 键名唯一性保障:使用 manufacture:make 作为二级键,确保不同厂商下同名 make(如 Honda:Accord 和 Toyota:Accord)不会冲突;
- 避免重复引用:通过 !array.includes(node) 判断是否已加入父级数组,防止重复插入(尤其在数据含重复记录时);
- 可扩展性:若需支持四层(如加 trim 字段),只需新增一级缓存键(如 manufacture:make:model:trim)和对应逻辑;
- TypeScript 友好:可定义明确接口提升类型安全:
interface Model { model: string }
interface Make { make: string; models: Model[] }
interface Manufacture { manufacture: string; makes: Make[] }
- 性能对比:相比三重嵌套 find()(最坏 O(n³)),本方案稳定 O(n),10万条数据实测耗时
该方法兼顾可读性、性能与健壮性,是处理层级聚合类数据转换的推荐实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











