实用的 JavaScript 对象分组静态方法 Object.groupBy()
2025-01-22 08:19:30    888 字   
This post is also available in English and alternative languages.

转自:实用的JS对象分组静态方法Object.groupBy()


1. 原生分组方法Object.groupBy()

好消息,所有现代浏览器都已经支持浏览器原生的静态方法Object.groupBy()了,如下图所示,Safari浏览器支持最晚,今年4月份才开始支持。

1

这就意味着最晚明年,就算不使用Polyfill代码,也能在生产环境使用该方法了。


1.1. 作用

Object.groupBy()可以让可枚举对象,根据某个键进行自动分组。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const data = [{
id: 1,
name: '张三'
}, {
id: 3,
name: '李四'
}, {
id: 4,
name: '王二'
}, {
id: 2,
name: '张三'
}];

const result = Object.groupBy(data, ({ name}) => name);

console.log(result);

输出结果则是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{张三: Array(2), 李四: Array(1), 王二: Array(1)}
张三: Array(2)
0: {id: 1, name: '张三'}
1: {id: 2, name: '张三'}
length: 2
[[Prototype]]: Array(0)
李四: Array(1)
0: {id: 3, name: '李四'}
length: 1
[[Prototype]]: Array(0)
王二: Array(1)
0: {id: 4, name: '王二'}
length: 1
[[Prototype]]: Array(0)

截图如下:

2

1.2. 语法

Object.groupBy(items, callbackFn)

  • items:将被分组的可迭代对象。
  • callbackFn(element, index):为可迭代对象中的每个元素执行的函数。其返回值会被作为键,用来指向分组后的数组项。使用以下参数调用该函数。

1.3. Polyfill代码

如果要兼容陈旧浏览器,可以试试使用这段JavaScript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const hasGroup = typeof Object.groupBy === typeof undefined || typeof Array.groupToMap === typeof undefined || typeof Array.group === typeof undefined;
if (!hasGroup) {
const groupBy = (arr, callback) => {
return arr.reduce((acc = {}, ...args) => {
const key = callback(...args);
acc[key] ??= []
acc[key].push(args[0]);
return acc;
}, {});
};

if (typeof Object.groupBy === typeof undefined) {
Object.groupBy = groupBy;
}

if (typeof Array.groupToMap === typeof undefined) {
Array.groupToMap = groupBy;
}

if (typeof Array.group === typeof undefined) {
Array.group = groupBy;
}
}

2. 实际案例

我在某项目开发中已经使用过此API了,给大家演示下使用场景。

已知有数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const okrAlignList = [{
objectId: 1,
empNum: 'YW001',
empName: '刘一'
}, {
objectId: 3,
empNum: 'YW002',
empName: '姚二三'
}, {
objectId: 4,
empNum: 'YW003',
empName: '张鑫旭'
}, {
objectId: 2,
empNum: 'YW001',
empName: '刘一'
}]

然后页面渲染的时候,如果是同一人,是需要合并展示的,如配图所示:

3

此时,我们就可以直接使用Object.groupBy()方法进行渲染,而不需要自己额外写一个分组方法了,此时的Vue模板渲染使用下面的就可以了:

1
2
3
4
5
6
7
8
<data 
v-for="(value, key) in Object.groupBy(okrAlignList, obj => obj.empNum)"
:key="key"
>
{{ value[0].empName }}<output v-if="value.length > 1">
({{ value.length}})
</output>
</data>

代码简洁多了。


3. 还有Map.groupBy()方法

除了Object对象有groupBy()静态方法,Map对象也有,兼容性一致,语法也是一样的。

使用示意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const inventory = [
{ name: 'asparagus', type: 'vegetables', quantity: 9 },
{ name: 'bananas', type: 'fruit', quantity: 5 },
{ name: 'goat', type: 'meat', quantity: 23 },
{ name: 'cherries', type: 'fruit', quantity: 12 },
{ name: 'fish', type: 'meat', quantity: 22 },
];

const restock = { restock: true };
const sufficient = { restock: false };
const result = Map.groupBy(inventory, ({ quantity }) =>
quantity < 6 ? restock : sufficient,
);
console.log(result.get(restock));
// [{ name: "bananas", type: "fruit", quantity: 5 }]

Map.groupBy()使用用在分组信息会随时间变化的场景下,因为即使对象被修改,它仍将继续作为返回Map的键。

其他时候,换成使用Object.groupBy()实现也是可以的。