可用reduce配合对象或Map实现groupBy:对象适合字符串/数字键,需手动初始化空数组;Map更健壮,支持Symbol、对象等键类型,推荐用于严格类型场景。

可以用 reduce 配合对象或 Map 作为累加器,遍历数组时根据分组键将元素归类到对应桶中,实现类似 groupBy 的效果。
用对象做累加器(最常用)
适合分组键是字符串、数字等可安全作为对象属性名的场景。注意初始化累加器为一个空对象,并在每次迭代中确保目标分组数组存在。
- 初始化累加器为
{} - 对每个元素计算分组键(如
item.category) - 若该键在累加器中尚无对应数组,先赋值为空数组
acc[key] = [] - 再把当前元素推入
acc[key].push(item)
示例:
const list = [
{ name: 'Alice', dept: 'HR' },
{ name: 'Bob', dept: 'IT' },
{ name: 'Charlie', dept: 'HR' }
];
const grouped = list.reduce((acc, item) => {
const key = item.dept;
if (!acc[key]) acc[key] = [];
acc[key].push(item);
return acc;
}, {});
// 结果:{ HR: [...], IT: [...] }
用 Map 做累加器(更健壮)
当分组键可能是对象、Symbol、null 或需要严格区分 '1' 和 1 时,推荐用 Map,避免对象键隐式类型转换问题。
- 初始化累加器为
new Map() - 用
map.get(key) ?? []获取当前分组数组(ES2020+ 空值合并) - 更新后用
map.set(key, newArray)写回 - 最后可选调用
Object.fromEntries(map)转为普通对象
示例:
const groupedMap = list.reduce((map, item) => {
const key = item.dept;
const group = map.get(key) ?? [];
map.set(key, [...group, item]);
return map;
}, new Map());
// 转成对象(可选):Object.fromEntries(groupedMap)
封装成可复用的 groupBy 函数
把分组逻辑抽离为高阶函数,接收数组和一个取键函数(类似 Lodash 的 groupBy API)。
- 参数:数组 +
keyFn(如item => item.status或'type') - 内部自动处理字符串路径(如
'user.name')可用简单解析或_.get替代 - 返回分组后的对象
简易实现:
function groupBy(array, keyFn) {
if (typeof keyFn === 'string') {
const path = keyFn.split('.');
keyFn = item => path.reduce((o, k) => o?.[k], item);
}
return array.reduce((acc, item) => {
const key = keyFn(item);
const bucket = acc[key] ??= [];
bucket.push(item);
return acc;
}, {});
}
// 使用:groupBy(list, 'dept') 或 groupBy(list, x => x.dept.toUpperCase())
注意事项与避坑
实际使用中容易忽略几个细节:
- 不要直接用
acc[key].push(...)而不检查acc[key]是否存在,否则报Cannot read property 'push' of undefined - 避免在 reduce 中修改原数组或外部变量,保持纯函数特性
- 如果分组键是布尔值、
null、undefined,对象方式会转成字符串('true'、'null'),此时应优先用Map - 性能敏感场景下,
Map的set/get比对象属性访问略慢但更稳定;一般业务无需过度优化


















