JavaScript 常用数据处理函数 groupBy

一、前言

数据处理与分析中,对数据进行分组是非常常见的功能,不论是实际工作中,还是在面试的场景中应用十分广泛。尤其在函数式编程中 groupBy 十分常见。在 JavaScript 中 groupBy 也已经进入了 JS 的标准。

二、介绍

groupBy 函数是一个用于对 数组元素 进行 分组 的实用函数。groupBy 允许我们指定分组条件,将数组的元素分为不同的组,用于数据处理或者展示。

三、标准

proposal-array-grouping : tc39.es/proposal-ar...

  • Object.groupBy
ts 复制代码
Object.groupBy(items, callbackFn) // 语法,第一个参数是可迭代对象,第二参数是回调函数
  • Map.groupBy
ts 复制代码
Map.groupBy(items, callbackFn) // 语法,第一个参数是可迭代对象,第二参数是回调函数

四、环境支持

五、Object.groupBy 示例

ts 复制代码
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 据分组

虽然函数通用性更加广泛,但是复杂度比直接传递数据要稍微高一点,这里实现一个按照传递属性方式实现一个 groupBy:

ts 复制代码
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 实现 groupBy 之后,实现通用的 groupBy 就比较容易了:

ts 复制代码
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
  }, {})
}

这里用 TS 实现主要约束类型,本质也非常简单就是 keyFunc 用当前的 obj 执行获取 key,如果没有获取到了,设置一个空的数组初始化,并将数据填充进去。重复累积这个过程。

八、lodash z中的 groupBy

ts 复制代码
_.groupBy(data, (item) => item.type)

lodash 将 groupBy 放在 collection 集合中,而非数组中。实用方式与以上基本一致。

九、rxjs groupBy

如果是函数式编程,当然离不开 RxJS 中数据操作。rxjs groupBy 不能单独的实现 js 中 groupBy 的功能,当然我们组合操作符或者自定义操作符实现。以下是实现一个自定义操作符 customGroupBy

ts 复制代码
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 中的 groupBy 函数实用特性。从 ES 标准草案,到自己实现一个简单的 groupBy, 再到 lodash 以及 RxJS 中实现 groupBy 效果。在函数式编程中,groupBy 实用特性非常高,在数据处理领域广泛适用。

相关推荐
@大迁世界5 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路13 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
勇哥java实战分享15 分钟前
短信平台 Pro 版本 ,比开源版本更强大
后端
是一个Bug17 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213819 分钟前
React面向组件编程
开发语言·前端·javascript
学历真的很重要19 分钟前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
计算机毕设VX:Fegn089522 分钟前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计
上进小菜猪38 分钟前
基于 YOLOv8 的智能杂草检测识别实战 [目标检测完整源码]
后端
持续升级打怪中40 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路44 分钟前
GDAL 实现矢量合并
前端