ES2024系列 - Object.groupBy() 数组分组聚合

211 阅读3分钟

新特性解决的问题

我们在处理数据库返回的数据时,时常要做各种分组聚合,举例如下:

const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 5 },
  { name: "bananas", type: "fruit", quantity: 0 },
  { name: "goat", type: "meat", quantity: 23 },
  { name: "cherries", type: "fruit", quantity: 5 },
  { name: "fish", type: "meat", quantity: 22 },
];

假如现在数据库中存储了上述的各种不同的食物名称、分类和数量。

现在UI界面要求按照不同分类分别列出食物和其数量。 对于前端来说,肯定是希望这样一个数据结构:

{
  vegetables: [
    { name: 'asparagus', type: 'vegetables', quantity: 5 },
  ],
  fruit: [
    { name: "bananas", type: "fruit", quantity: 0 },
    { name: "cherries", type: "fruit", quantity: 5 }
  ],
  meat: [
    { name: "goat", type: "meat", quantity: 23 },
    { name: "fish", type: "meat", quantity: 22 }
  ]
}

以上数据结构按照蔬菜 水果 对数据进行了分类,这样前端按照不同分类列出食物和数量就变得非常轻松了。

要做这种处理,在以前我们需要另外定义一个空对象,然后遍历数据往对象里添加键和数组值,要做非空判断。虽然不难,但是繁琐,费神。

现在我们有了Object.groupBy,一切就变得简单了:

const result = Object.groupBy(inventory, ({ type }) => type);

一行代码搞定。这行代码会按照type来做分组聚合。

这就是新特性:Object.groupBy

如果你用过Lodash,相信你已经对这种功能比较熟悉了。

Object.groupBy

从上面的例子可以看出,Object.groupBy可以对目标数据集进行分组聚合。

下面咱们来看一下它的具体用法。

语法

Object.groupBy(items, callbackFn)

参数

items

第一个参数items代表需要处理的可迭代对象,例如:数组。

callbackFn

第二个参数callbackFn代表callback函数。 callbackFn的参数: 1. element:数组中的元素。 2. indexelement的数组索引。

callbackFn的返回值规则:

callbackFn必须有返回值,每个返回值都会作为最终返回对象的键名。如果多条数据的键名相同,则其值以数组的形式叠加起来。 由于是用来做键名,因此返回值最好是字符串或者Symbol类型,如果不是这两种类型之一,将被强制转换成字符串类型。 如果没有返回值,那undefined会被作为返回值。

返回值

返回值是一个对象,对象的键名是callbackFn函数的返回值,键值是每个callbackFn函数的返回值所对应的数组项组成的数组。

所以说Object.groupBy做的事就是对目标数组进行分组。

返回对象的原型是null,而不是Object.prototypeObject.create(null)创建的就是一个原型为null的对象。 这就意味着对象中常用的方法不能在Object.groupBy上使用。 例如:

const o = Object.groupBy(...);
o.hasOwnProperty('xxx');

这里会报错hasOwnProperty不是一个函数。

像这样创建的对象,其原型是Object.prototype,具有hasOwnProperty方法:

let o = {};
// true
Object.getPrototypeOf(o) === Object.prototype;

// o.hasOwnProperty 存在

为什么要这样呢?是因为怕对象中的属性和Object.prototype的属性重名导致冲突,所以干脆抛弃了Object

文字表达太苍白了,说得太多只会让大家困扰,很不容易理解。我们再用一个最简洁的例子来说明它的用法:

const array = [1, 2, 3, 4, 5];
const groupedResult = Object.groupBy(array, (num, index) => {
  return num % 2 === 0 ? 'even': 'odd';
});
// groupedResult是: { odd: [1, 3, 5], even: [2, 4] }

相信看到这里你已经知道它的用法了。

为什么不是Array.groupBy或者Array.prototype.groupBy

其实上面咱们也说过,groupBy其实不只是适用于数组,它适用于所有的可迭代对象,另外它返回的是一个对象,因此放在Object下是比较合适的。

浏览器兼容性

目前该特性已经到了 Stage 4 阶段,欲了解 Stage 4 的含义可以看这篇文章 各大主流浏览器也是刚刚开始支持。

Polyfill

corejs已经有对应的Polyfill。

结束语

ES2024系列专辑将持续更新将在2024年发布的新特性。敬请关注!

注:以上例子部分参考了MDN