JS 原生支持最新分组方法(译)

大家好,这里是大家的林语冰。

数组元素(item)分组可能是您已经反复实践的事情之一。您每次都会手写一个分组函数,或者复制粘贴 lodash 的 groupBy 函数。

好消息是: JS(JavaScript)现在支持分组方法,所以您不必再做"代码搬运工"了。Object.groupByMap.groupBy 是全新的方法,更易于分组,解放双手且降本增效。

免责声明

本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请临幸 JAVASCRIPT IS GETTING ARRAY GROUPING METHODS

直到现在的分组

假设您有一个代表人物的对象数组,并且您希望按年龄对它们进行分组。您可以使用 forEach 循环,如下所示:

js 复制代码
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,如下所示:

js 复制代码
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 方法,您可以一行搞定:

js 复制代码
const peopleByAge = Object.groupBy(people, person => person.age)

草履虫都能学会!虽然有若干注意事项。

Object.groupBy 返回一个 null-prototype 对象(空原型对象)。这意味着,该对象不会从 Object.prototype 继承任何属性。这很美滋滋,因为这意味着,您不会意外覆盖 Object.prototype 的任何属性,但这也意味着,该对象没有您可能需要的任何方法,比如 hasOwnPropertytoString

js 复制代码
const peopleByAge = Object.groupBy(people, person => person.age)
console.log(peopleByAge.hasOwnProperty('28'))
// TypeError: peopleByAge.hasOwnProperty 不是函数

传递给 Object.groupBy 的回调函数应返回 Symbolstring。如果它返回任何其他内容,它将被强制转换为 string

在我们的栗子中,我们始终将 age 作为 number 返回,但在结果中,它被强制为 string。尽管您仍然可以使用方括号表示法访问 number 属性,但也会将参数强制为 string

js 复制代码
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。这意味着,您可以使用所有常用的 Map 函数。这也意味着,您可以从回调函数返回任何类型的值。

js 复制代码
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 中检索元素,对象必须具有相同的标识。

js 复制代码
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 中成功检索元素,请确保保留要用作键的对象的引用。

何时可用?

这两种 groupBy 方法目前乃 TC39 提案 stage 3 的一部分。这意味着,它很有可能成为标准,因此,出现了若干实现。

Chrome 117 刚刚推行,支持这两种方法,Firefox 在 119 版本中发布了支持。Safari 以不同的名称实现了这些方法,我相信它们很快就会更新。由于这些方法在 Chrome 中,这意味着,它们已在 V8 中实现,因此下次 V8 更新时将在 Node 中可用。

为什么设计为静态方法?

您可能想知道为什么将其实现为 Object.groupBy 而不是 Array.prototype.groupBy。根据该提案,有一个库对 Array.prototype 使用了不兼容 groupBy 的方法的"猴子补丁"(monkey patch)。在考虑新的 Web API 时,向后兼容十分重要。几年前,在尝试实现 Array.prototype.flatten 时,在一个 SmooshGate 的事件中强调了这一点。

幸运的是,使用静态方法实际上似乎更适合将来的可扩展性。当 Records 和 Tuples 提案实现时,我们可以添加一个 Record.groupBy 将数组分组为不可变记录的方法。

JS 正在填补空白

将元素分组在一起显然是我们作为开发者的必由之路。lodash.groupBy 目前 npm 周下载量高达 1_500_0002_00_000。很高兴看到 JS 取而代之,让我们解放双手,降本增效。

现在,请到 Chrome 117 中亲自尝试这些新方法。语冰空谈终觉浅,极客撸码要宫刑。

您现在收看的是《前端翻译计划》,学废了的小伙伴可以订阅此专栏合集,我们每天佛系投稿,欢迎持续关注前端生态。谢谢大家的点赞,掰掰~

相关推荐
迷雾漫步者1 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240255 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人5 小时前
前端知识补充—CSS
前端·css
GISer_Jing6 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试