在日常开发中,很多时候须要对数组进行分组,每次都要手写一个分组函数,或者应用lodash的groupBy
函数。好消息是,JavaScript 当初正在引入全新的分组办法:Object.groupBy
和Map.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
上的任何属性或办法,例如hasOwnProperty
或toString
等。尽管这样做能够防止意外笼罩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.groupBy
和Object.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
的事件。
应用静态方法实际上对将来的可扩展性更好。当Records
和Tuples
提案实现时,能够增加一个Record.groupBy
办法,用于将数组分组为不可变记录。
简而言之,应用静态方法能够更好地放弃向后兼容性,并提供更好的扩展性,以便在将来增加更多功能和数据结构。
JavaScript 正在填补这些空白,并使咱们的开发更简略。目前,lodash.groupBy
每周的 npm 下载量在 150 万至 200 万次之间,当所有浏览器都反对该办法之后,就不再须要引入lodash.groupBy
库了!