reduce分组核心是累积成以属性值为key的对象,每个key对应子数组;可封装为支持字符串或函数参数的通用函数,并能转为键值对数组格式。

用 reduce 按指定属性对数组分组,核心是把原数组“累积”成一个以分组键为 key 的对象(或 Map),每个 key 对应一个子数组。
基础写法:返回对象形式的分组结果
假设有一个用户数组,想按 city 分组:
const users = [
{ name: '张三', city: '北京' },
{ name: '李四', city: '上海' },
{ name: '王五', city: '北京' },
{ name: '赵六', city: '广州' }
];
const grouped = users.reduce((acc, item) => {
const key = item.city; // 指定分组依据的属性
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {}); // 初始值设为空对象
// 结果:
// {
// 北京: [{name:'张三',city:'北京'}, {name:'王五',city:'北京'}],
// 上海: [{name:'李四',city:'上海'}],
// 广州: [{name:'赵六',city:'广州'}]
// }
通用封装函数:支持任意属性名
把逻辑抽成可复用函数,传入数组和属性名字符串即可:
function groupBy(arr, prop) {
return arr.reduce((acc, item) => {
const key = typeof prop === 'function'
? prop(item)
: item[prop];
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {});
}
// 使用示例
const byCity = groupBy(users, 'city');
const byFirstLetter = groupBy(users, item => item.name[0]); // 按姓名首字母
- 支持字符串属性名(如
'city')和函数(如提取首字母、处理 null 等) - 自动处理属性不存在的情况(此时
key是undefined,会归到acc.undefined组;如需过滤,加个if (key == null) return acc;)
进阶:返回数组形式([{key: '北京', values: [...]}, ...])
如果后端或 UI 需要结构更明确的数组格式,可以在 reduce 后再转换:
立即学习“Java免费学习笔记(深入)”;
const groupedArray = Object.entries(groupBy(users, 'city'))
.map(([key, values]) => ({ key, values }));
// 结果:
// [
// { key: '北京', values: [...] },
// { key: '上海', values: [...] },
// { key: '广州', values: [...] }
// ]
或者一步到位,在 reduce 中直接构造这种结构(但不如先生成对象再转清晰)。
注意点与常见坑
-
初始值必须是对象(
{})或 Map,不能是数组,否则acc[key]会出错 -
属性值类型影响 key 的匹配:比如
1和'1'是不同 key;建议确保数据类型一致,或提前统一(如都转成字符串) -
避免在 reduce 中修改原对象,尤其是用
push时,如果希望不污染原始项,可用扩展运算符浅拷贝:acc[key].push({...item}) - 如果需要去重分组、合并字段等复杂逻辑,可在
push前加判断或做聚合计算


















