JavaScript – Object.groupBy & Map.groupBy
前言
group by 是一个很常见的功能,但 JS 却没有 build-in 的方法,一直到 es2024 才有 Object.groupBy (前生是 Array.prototype.group) 和 Map.groupBy (前生是 Array.prototype.groupToMap)。
目前所有 modern browser 都支持了这个功能。

如果想兼容 IOS 17.4 以下,可以使用 core-js polyfill。
参考
Stack Overflow – Most efficient method to groupby on an array of objects (用 reduce 实现的 group by, 也是最 popular 的方案)
Setup Polyfill
我需要兼容 IOS,所以例子我就搭配 core-js 呗。
yarn add core-js
然后 import
import 'core-js/actual/map/group-by';
import 'core-js/actual/object/group-by';
这样就可以了
Object.groupBy
Object.groupBy 是一个静态方法,它的调用接口长这样
groupBy<K extends string | number | symbol, T>(
items: Iterable<T>,
keySelector: (item: T, index: number) => K,
): Partial<Record<K, T[]>>;
groupBy 的用法很简单, 给一个 item array,配上一个 select key 函数,返回一个 key,它会把相同 key 的 item group 在一起。
最终返回一个对象 (non-prototype object),对象的 key 就是 group by 的 key,value 则是相同 key 的所有 items。
注:key 的类型必须是 string | symbol | number,其它的最好不要,如果 select key 函数返回的不是 string | symbol 会自动被强转成 string。
例子:group by name
const items = [
{ name: 'Derrick', age: 1 },
{ name: 'Peter', age: 1 },
{ name: 'Derrick', age: 2 },
{ name: 'Peter', age: 2 },
];
const result = Object.groupBy(items, item => item.name);
console.log(JSON.stringify(result,
结果

Map.groupBy
groupBy<K, T>(
items: Iterable<T>,
keySelector: (item: T, index: number) => K,
): Map<K, T[]>;
它和 Object.groupBy 差不多,只是 group key 不强制要求必须是 string | number | symbol,group key 可以是任何类型,而且不会自动强转成 string。
它之所以可以是任何类型是因为 Map.groupBy 返回的结果不是 Object,而是 Map,Map 的 key 可以是任何类型。
const items = [
{ name: 'Derrick', age: 1 },
{ name: 'Peter', age: 1 },
{ name: 'Derrick', age: 2 },
{ name: 'Peter', age: 2 },
];
const result = Map.groupBy(items, item => item.name);
for (const [key, value] of result) {
// 1. ['Derrick', [{ name: 'Derrick', age: 1 }, { name: 'Derrick', age: 2 }]]
// 2. ['Peter', [{ name: 'Peter', age: 1 }, { name: 'Peter', age: 2 }]]
console.log([key, value]);
}
How It Group?
group by 的关键之一就是 group key 的 comparison。
比如我 fetch 一些资料,然后想 group by Date。
如果使用 Object.groupBy 的话,它会先把 Date 转成 string 然后放入对象的 key (利用对象 key unique 特性来 group,可以理解为 key1 === key2 就 group 在一起)
如果是 Map.groupBy 则是放入 Map 的 Key,这里和 Object.groupBy 有一个微小的区别,它不会把 Date 强转成 string,所以 key1 === key2 对比的是 Date object 而不是 Date string。
通常,我们的直觉会认为是,相同的 date time value group 在一起,而不是相同指针 group 在一起,这样用 Map.groupBy 的结果就是错误的。
const today1 = new Date(2023, 0, 26);
const today2 = new Date(2023, 0, 26);
const items = [
{ date: today1, age: 1 },
{ date: today1, age: 1 },
{ date: today2, age: 2 },
];
const result1 = Object.groupBy(items, item => item.date as unknown as string);
console.log(Object.keys(result1).length); // 1 const result2 = Map.groupBy(items, item => item.date);
console.log([...result2.keys()].length); // 2
所以在使用 Object.groupBy 或 Map.groupBy 时,一定要注意 group key 的类型。
How It Order?
上面提到了,Object.groupBy 返回的是 object,而 object 的 key 是很难确保顺序的,参考: Object.keys(..)对象属性的顺序? (number first, order by create, symbol last)
const people = [
{ name: 'derrick', age: 11 },
{ name: 'derrick', age: 15 },
{ name: '1148', age: 22 },
{ name: '1148', age: 18 },
];
console.log(Object.keys(Object.groupBy(people, person => person.name))); // ['1148', 'derrick']
console.log([...Map.groupBy(people, person => person.name).keys()]); // ['derrick', '1148']
Object.keys 的顺序是数字优先的 (哪怕是 string number 也同样优先...), 所以,如果想依据 array 原本的顺序那么请尽可能使用 Map.groupBy。
Multiple Group Key
C# LINQ GroupBy 支持 multiple group key
items.GroupBy(item => new { item.Name, item.Age }).ToList()
但 JS 没有这个功能,勉强要实现的话可以用 JSON.stringify,只是性能差又不优雅...
分享我以前写的 group by multiple key

function groupByMultipleKey<TItem, TKeys extends unknown[]>(
items: TItem[],
keySelector: (item: TItem, index: number) => [...TKeys],
): Map<[...TKeys], TItem[]> {
const resultMap = new Map<[...TKeys], TItem[]>(); for (let index = 0; index < items.length; index++) {
const item = items[index]; const itemKeys = keySelector(item, index); const existedKey = [...resultMap.keys()].find(resultKeys =>
resultKeys.every((resultKey, index) => isSameKey(resultKey, itemKeys[index])),
); if (existedKey !== undefined) {
resultMap.get(existedKey)!.push(item);
} else {
resultMap.set(itemKeys, [item]);
}
}
return resultMap; function isSameKey(key1: unknown, key2: unknown): boolean {
if (key1 === key2) return true;
if (Number.isNaN(key1) && Number.isNaN(key2)) return true;
if (key1 instanceof Date && key2 instanceof Date) {
return key1.getTime() === key2.getTime();
}
return false;
}
}
JavaScript – Object.groupBy & Map.groupBy的更多相关文章
- ES6新特性:Javascript中的Map和WeakMap对象
Map对象 Map对象是一种有对应 键/值 对的对象, JS的Object也是 键/值 对的对象 : ES6中Map相对于Object对象有几个区别: 1:Object对象有原型, 也就是说他有默认的 ...
- JavaScript中的Map
1.首先,在新版本的浏览器中,已经实现了对Map的原生支持:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glob ...
- 由实现JavaScript中的Map想到的
项目中要用到JavaScript中的Map数据类型,它不像JDK那样有自带的,怎么办?搜了找到一个不错的(http://darkmasky.iteye.com/blog/454749).用这个可以满足 ...
- 【EatBook】-NO.1.EatBook.1.JavaData.1.001-《JSON 必知必会-Introduction to JavaScript Object Notation》-
1.0.0 Summary Tittle:[EatBook]-NO.1.EatBook.1.JavaData.1.001-<JSON 必知必会-Introduction to JavaScrip ...
- JavaScript 中的 Map
很多编程语言中都有类似Map这种 键-值对 的数据结构. 可惜,JavaScript没有. 幸运的是,可以自己构建一个Map对象. 对象的定义 <script type="text/j ...
- JSON,全称:JavaScript Object Notation,作为一个常见的轻量级的数据交换格
JSON,全称:JavaScript Object Notation,作为一个常见的轻量级的数据交换格式,应该在一个程序员的开发生涯中是常接触的.简洁和清晰的层次结构使得 JSON 成为理想的数据交换 ...
- JSON: JavaScript Object Notation
JSON是JavaScript Object Notation 的缩写,是JS提供的一种数据交换格式.1) JSON对象本质上就是一个JS对象,但是这个对象比较特殊,它可以直接转换为字符串,在不同语言 ...
- 【java】之3种方式实现Object和Map之间的转换
利用commons.BeanUtils实现Obj和Map之间转换,这种是最简单,也是最经常用的 public static Object mapToObject(Map<String, Obje ...
- Javascript Object、Function对象
1.Object对象 原型对象 原型是对象的一个属性,也就是prototype属性,每个对象都有这个内部属性,而且他本身也是一个对象. <script type="text/javas ...
- XML.ObjTree -- XML source code from/to JavaScript object like E4X
转载于:http://www.kawa.net/works/js/xml/objtree-try-e.html // ========================================= ...
随机推荐
- 薅 AWS 羊毛的船新方式,以 ChatBot 为例
还在担心一年免费服务器到期后该怎么办?(Solo社区 投稿) 网上绝大多数薅 AWS 羊毛的教程都是在教大家如何申请创建一年免费的 VPS,太 OUT 了!就问一个问题,一年到期了那咋办? 其实,除了 ...
- VPS测试脚本,网络线路,路由测试,流媒体服务器测试脚本
收集了一些服务器测试脚本,测试性能,网络以及解锁Netflix等服务.记录收集一下,特此记录. yabs测试脚本wget -qO- yabs.sh | bash 老外比较爱用的服务器性能测试脚本. ...
- Vscode 一次选中多行 光标一次定位多行
1 . 鼠标点击开始位置(定位到行首时,鼠标就点击第一行的行首:定位到行尾时,鼠标就点击第一行的行尾:) 2. 按住shift+alt 点击结束的位置(定位到行首时,鼠标就点击最后一行的行首:定位到 ...
- [oeasy]python0025_ 顺序执行过程_流水_流程_执行次序
顺序执行过程_流水_流程_执行次序 回忆上次内容 上次 熟悉了 vim编辑器 操作 作用 w 向前移动光标一个word b 向后移动光标一个word :r oeasy.py 读取文件到当前文件缓存 g ...
- 顺序表_C
// Code file created by C Code Develop #include "ccd.h" #include "stdio.h" #incl ...
- 网络基础 CAS协议学习总结
架构介绍 系统组件 CAS服务器和客户端构成了CAS系统体系结构的两个物理组件,它们通过各种协议进行通信. CAS服务器 CAS服务器是基于Spring Framework构建的Java servle ...
- Python 按比例获取样本数据或执行任务
按比例获取样本数据或执行任务 By:授客 QQ:1033553122 开发环境 win 10 python 3.6.5 需求 已知每种分类的样本占比数,及样本总数,需要按比例获取这些分类的样本.比如, ...
- 2023/4/19 SCRUM个人博客
1.我昨天的任务 初步了解了pandas库,对series和dataframe有了初步的学习使用 2.遇到了什么困难 对PYQT5的概念没有定义,准备进行学习 3.我今天的任务 学习了PYQT5的部分 ...
- 【WebSocket】一个简单的前后端交互Demo
WebSocket资料参考: https://www.jianshu.com/p/d79bf8174196 使用SpringBoot整合参考: https://blog.csdn.net/KeepSt ...
- 【SpringBoot】07 探索配置方式 Part3 多环境配置
1.按多个Profile文件来配置 SpringBoot默认会使用第一个 我们可以在默认的application.properties中设置激活哪种环境配置 profile的命名规则 2.按Yml可以 ...