
本文详解如何使用 javascript 的 reduce 方法将数组按某个字段(如 city)分组,最终输出符合需求的嵌套数组结构——每个元素为 { city, data: [...] } 对象,避免常见误区中返回普通对象的问题。
本文详解如何使用 javascript 的 reduce 方法将数组按某个字段(如 city)分组,最终输出符合需求的嵌套数组结构——每个元素为 { city, data: [...] } 对象,避免常见误区中返回普通对象的问题。
在实际开发中,我们常需将扁平数组按某一属性(如 city)聚类,并组织成更具语义化的层级结构。不同于简单的 Object.groupBy(ES2024 提案)或 reduce 构建键值对对象的方式,本文目标是直接生成一个数组,其中每个元素代表一个分组,包含分组标识(如 city)和该组下所有原始项组成的 data 数组。
关键在于:reduce 的初始值与归并逻辑必须保持类型一致——若期望最终结果为数组,则累加器(groups)的初始值必须是空数组 [],且每次迭代都应返回该数组(而非新对象或普通字面量对象)。
以下是实现该需求的标准方案:
const locations = [
{"city": "London", "district": "Brixton", "id": "Eue3uFjUHKi6wh73QZLX"},
{"city": "Manchester", "district": "Bury", "id": "QZiiUBgzZaJt2HcahELT"},
{"city": "London", "district": "Hackney", "id": "v2itdyO4IPXAMhIU8wce"}
];
const groupedLocations = locations.reduce((groups, item) => {
const { city } = item;
// 在当前 groups 数组中查找是否存在同 city 的分组
const existingGroup = groups.find(group => group.city === city);
if (existingGroup) {
// 若已存在,直接推入 data
existingGroup.data.push(item);
} else {
// 若不存在,新建分组并加入 groups 数组
groups.push({ city, data: [item] });
}
return groups; // ✅ 始终返回数组,确保 reduce 结果类型稳定
}, []);执行后,groupedLocations 将精确匹配预期输出:
[
{
"city": "London",
"data": [
{"city": "London", "district": "Brixton", "id": "Eue3uFjUHKi6wh73QZLX"},
{"city": "London", "district": "Hackney", "id": "v2itdyO4IPXAMhIU8wce"}
]
},
{
"city": "Manchester",
"data": [
{"city": "Manchester", "district": "Bury", "id": "QZiiUBgzZaJt2HcahELT"}
]
}
]✅ 注意事项与优化建议:
- 性能考量:find() 在大数据量时为 O(n²),若数组长度超千级,可先用 Map 缓存索引提升至 O(n),再转为数组;但对多数前端场景,上述写法简洁、可读性强,推荐优先使用。
- 不可变性需求:如需函数式风格(不修改原数组),可改用 reduce + 展开语法创建新数组,但会略微增加内存开销。
- 健壮性增强:生产环境建议校验 item.city 是否为字符串/有效值,避免 undefined 导致分组异常。
- 扩展性提示:此模式可轻松适配任意字段(如 category、status),只需替换 city 为对应 key 即可复用。
总结:核心在于理解 reduce 的类型守恒原则——初始值决定输出类型,逻辑中持续维护该类型。掌握这一思想,即可灵活构造各类结构化分组结果,无需依赖第三方库或冗余中间对象。










