本文详解 JavaScript 中 Object.groupBy 与 Map.groupBy 的标准用法,涵盖 TC39 提案进展与环境支持。通过从零实现基于 Key 和通用函数的分组逻辑,对比 Lodash 与 RxJS 中的分组方案,并展示如何在函数式编程中自定义操作符,全面解析数据处理中的分组技巧。
一、前言
在数据处理与分析领域,对数据进行分组是一项极为常见且核心的功能,无论是日常业务开发还是技术面试中,其应用场景都十分广泛。特别是在函数式编程范式下, 更是不可或缺的基础工具。目前,该功能已被纳入 JavaScript 语言标准,成为原生支持的能力。
二、介绍
groupBy 函数是一个用于对 数组元素 进行 分组 的实用工具。通过 groupBy,我们可以灵活指定分组条件,将数组中的元素划分为不同的组,从而满足复杂的数据处理或界面展示需求。
三、标准

该功能源自 TC39 提案:proposal-array-grouping: tc39.es/proposal-array-grouping/#sec-scope
Object.groupBy
Object.groupBy(items, callbackFn) // 语法,第一个参数是可迭代对象,第二参数是回调函数
Map.groupBy
Map.groupBy(items, callbackFn) // 语法,第一个参数是可迭代对象,第二参数是回调函数
四、环境支持

五、Object.groupBy 示例
const data = [
{ id: 1, type: 'A' },
{ id: 2, type: 'B' },
{ id: 3, type: 'A' },
{ id: 4, type: 'C' },
{ id: 5, type: 'B' },
];
const groupType = Object.groupBy(data, 'type') // ❌ 第二个参数必须是函数
const groupType = Object.groupBy(data, (item) => item.type) // ✅ 按照类型进行分类, 并且 type 就是 group 的 key 值。
// {
// A: [{…}, {…}]
// B: [{…}, {…}]
// C: [{…}]
/ }
六、实现一个按照给定 key 进行分组
虽然通用函数具备更广泛的适用性,但其实现复杂度略高于直接传递属性名的方式。这里展示一种通过传递属性名来实现 的方法:
function groupBy(arr, key) {
return arr.reduce((acc, obj) => {
const groupKey = obj[key];
acc[groupKey] = acc[groupKey] || [];
acc[groupKey].push(obj);
return acc;
}, {});
}
// data 复用以上的 data
groupBy(data, 'type') // 输出结果与 Object.groupBy 一致
实现的核心思想是利用 reduce 进行 累加。从初始化一个空对象 {} 开始,随后在对象上为对应的 key 赋值空数组,若元素符合该 key,则将其归入此分组。
七、实现一个通用的 groupBy
在掌握基于 key 的实现方式后,进一步实现支持自定义函数的通用 便相对容易:
type KeyFunc<T> = (obj: T) => string
export function groupByWithFn<T>(
array: T[],
keyFunc: KeyFunc<T>
): Record<string, T[]> {
return array.reduce((acc: Record<string, T[]>, obj: T) => {
const key: string = keyFunc(obj)
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
此处使用 TypeScript 实现,主要目的是约束类型。其本质逻辑非常直观:利用 keyFunc 对当前的 obj 执行以获取 key,若未获取到对应的 key,则初始化一个空数组,并将数据填充进去。通过重复累积这一过程完成分组。
八、lodash 中的 groupBy
_.groupBy(data, (item) => item.type)
在 lodash 库中, 被放置在 collection 集合模块中,而非仅针对数组。其使用方式与上述原生实现基本一致。
九、rxjs groupBy
若涉及函数式编程,RxJS 中的数据操作自然不可或缺。RxJS 原生的 groupBy 无法直接等同于 JS 中的 功能,但我们可以组合操作符或自定义操作符来实现。以下是实现一个自定义操作符 customGroupBy 的示例:
import { Observable, of } from 'rxjs';
import { map, mergeMap, reduce } from 'rxjs/operators';
// 自定义 groupBy 操作符
function customGroupBy(keySelector) {
return function (source) {
return new Observable((observer) => {
source.pipe(
mergeMap((arr) => arr),
reduce((acc, val) => {
const key = keySelector(val);
acc[key] = acc[key] || [];
acc[key].push(val);
return acc;
}, {})
).subscribe({
next: (result) => {
observer.next(result);
observer.complete();
},
error: (err) => observer.error(err),
});
});
};
}
of([
{ id: 1, type: 'A' },
{ id: 2, type: 'B' },
{ id: 3, type: 'A' },
{ id: 4, type: 'C' },
{ id: 5, type: 'B' },
]).pipe(
customGroupBy((obj) => obj.type)
).subscribe({
next: (v) => {console.log(v)}
});
customGroupBy 操作符接受一个函数作为参数,该函数是一个闭包,包含了 of 操作符中的静态数据。customGroupBy 是典型的自定义操作符实现,source 即为由 of 创建的静态可观察对象。后续逻辑同样利用 reduce 进行累加计算,最终对外输出数据。
十、groupBy 其他
- 在 SQL 中,GROUP BY 语句用于将行分组为汇总行。
- 在 Python 中,可以使用
itertools.groupby对可迭代对象进行分组。 - 在一些数据处理工具中,例如 Excel 以及 Python 的相关库,也提供了类似功能。
十一、小结
本文主要介绍了 JavaScript 中 函数的实用特性。内容从 ES 标准草案出发,涵盖了自己实现简单的 ,以及 lodash 和 RxJS 中实现 效果的方法。在函数式编程中, 的实用价值极高,在数据处理领域有着广泛的应用。
到此这篇关于 JavaScript 中常用数据处理函数 groupBy 的用法详解的文章就介绍到这了,更多相关 JavaScript groupBy 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!