在日常开发中,很多时候须要对数组进行分组,每次都要手写一个分组函数,或者应用lodash的groupBy函数。好消息是,JavaScript 当初正在引入全新的分组办法:Object.groupByMap.groupBy,当前再也不须要手写分组函数了,目前最新版本的 Chrome(117)曾经反对了这两个办法!

以前的数组分组

假如有一个由示意人员的对象组成的数组,须要依照年龄进行分组。能够应用forEach循环来实现,代码如下:

const people = [  { name: "Alice", age: 28 },  { name: "Bob", age: 30 },  { name: "Eve", age: 28 },];const peopleByAge = {};people.forEach((person) => {  const age = person.age;  if (!peopleByAge[age]) {    peopleByAge[age] = [];  }  peopleByAge[age].push(person);});console.log(peopleByAge);

输入后果如下:

{  "28": [{"name":"Alice","age":28}, {"name":"Eve","age":28}],  "30": [{"name":"Bob","age":30}]}

也能够应用reduce办法:

const peopleByAge = people.reduce((acc, person) => {  const age = person.age;  if (!acc[age]) {    acc[age] = [];  }  acc[age].push(person);  return acc;}, {});

无论哪种形式,代码都略显繁琐。每次都要查看对象,看分组键是否存在,如果不存在,则创立一个空数组,并将我的项目增加到该数组中。

应用 Object.groupBy 分组

能够通过以下形式来应用新的Object.groupBy办法:

const peopleByAge = Object.groupBy(people, (person) => person.age);

能够看到,代码十分简洁!

不过须要留神,应用Object.groupBy办法返回一个没有原型(即没有继承任何属性和办法)的对象。这意味着该对象不会继承Object.prototype上的任何属性或办法,例如hasOwnPropertytoString等。尽管这样做能够防止意外笼罩Object.prototype上的属性,但也意味着不能应用一些与对象相干的办法。

const peopleByAge = Object.groupBy(people, (person) => person.age);console.log(peopleByAge.hasOwnProperty("28"));// TypeError: peopleByAge.hasOwnProperty is not a function

在调用Object.groupBy时,传递给它的回调函数应该返回一个字符串或 Symbol 类型的值。如果回调函数返回其余类型的值,它将被强制转换为字符串。

在这个例子中,回调函数返回的是一个数字类型的age属性值,但因为Object.groupBy办法要求键必须是字符串或 Symbol 类型,所以该数字会被强制转换为字符串类型。

console.log(peopleByAge[28]);// => [{"name":"Alice","age":28}, {"name":"Eve","age":28}]console.log(peopleByAge["28"]);// => [{"name":"Alice","age":28}, {"name":"Eve","age":28}]

应用 Map.groupBy 分组

Map.groupByObject.groupBy简直做的是雷同的事件,只是返回的后果类型不同。Map.groupBy返回一个Map对象,而不是像Object.groupBy返回一个一般的对象。、

const ceo = { name: "Jamie", age: 40, reportsTo: null };const manager = { name: "Alice", age: 28, reportsTo: ceo };const people = [  ceo  manager,  { name: "Bob", age: 30, reportsTo: manager },  { name: "Eve", age: 28, reportsTo: ceo },];const peopleByManager = Map.groupBy(people, (person) => person.reportsTo);

这里依据人的汇报下级将他们进行了分组。如果想通过对象来从这个Map中获取数据,那么要求这些对象具备雷同的身份或援用。这是因为Map在比拟键时应用的是严格相等(===),只有两个对象具备雷同的援用,能力被认为是雷同的键。

peopleByManager.get(ceo);// => [{ name: "Alice", age: 28, reportsTo: ceo }, { name: "Eve", age: 28, reportsTo: ceo }]peopleByManager.get({ name: "Jamie", age: 40, reportsTo: null });// => undefined

在下面的例子中,如果尝试应用与ceo对象相似的对象作为键去拜访Map中的项,因为这个对象与之前存储在Map中的ceo对象不是同一个对象,所以无奈检索到对应的值。

浏览器反对

这两个groupBy办法是 proposal-array-grouping 提案提出的,该提案目前处于第3阶段,预计会在 2024 年成为正式规范。

9 月 12 日,Chrome 117公布,该版本反对了这两个办法。Firefox Nightly 在一个标记后曾经实现了这两个办法。Safari曾经以不同的名称实现了这些办法。因为这些办法在 Chrome 中可用,这意味着它们曾经在V8中被实现,所以下一次V8更新时它们将在Node中可用。

为什么要应用静态方法?

你可能会想,为什么这个性能被实现为Object.groupBy而不是Array.prototype.groupBy。依据提案,有一个库已经用不兼容的groupBy办法对Array.prototype进行了批改。在思考为Web新增API时,向后兼容性十分重要。几年前,在尝试实现Array.prototype.flatten时就呈现了一个称为SmooshGate的事件。

应用静态方法实际上对将来的可扩展性更好。当RecordsTuples提案实现时,能够增加一个Record.groupBy办法,用于将数组分组为不可变记录。

简而言之,应用静态方法能够更好地放弃向后兼容性,并提供更好的扩展性,以便在将来增加更多功能和数据结构。

JavaScript 正在填补这些空白,并使咱们的开发更简略。目前,lodash.groupBy每周的 npm 下载量在 150 万至 200 万次之间,当所有浏览器都反对该办法之后,就不再须要引入lodash.groupBy 库了!