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

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

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

好消息是: JS(JavaScript)现在支持分组方法,所以您不必再做"代码搬运工"了。Object.groupBy 和 Map.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 的任何属性,但这也意味着,该对象没有您可能需要的任何方法,比如 hasOwnProperty 或 toString。

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

传递给 Object.groupBy 的回调函数应返回 Symbol 或 string。如果它返回任何其他内容,它将被强制转换为 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_000 到 2_00_000 次。很高兴看到 JS 取而代之,让我们解放双手,降本增效。

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

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

相关推荐
迅猛龙办公室1 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
小狼154542 小时前
浏览器扩展脚本为什么有时候不生效:注入时机、iframe 和单页路由,多多开票助手
前端·chrome
じòぴé南冸じょうげん2 小时前
油猴脚本突然发现变成灰色了,无法使用?页面不生效?刷新页面没反应?
前端
Csvn4 小时前
组合式 API(Composition API)
前端
火柴就是我5 小时前
Android 打包报错 25.0.3
android·前端
BD_Marathon5 小时前
消息对象中字段的说明
java·前端·python
八荒启·交互动画5 小时前
Web特效025—用 Canvas 2D 做Web特效的定义与边界:这支画笔能做到哪一步
前端·webgl·网页特效·八荒启-交互动画·八荒启
Ai-_Man5 小时前
您您这可以把Dola的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。用AI导出鸭,答案是可以的
开发语言·前端·人工智能·小程序
excel5 小时前
Nuxt 中使用 useHead 优化 SEO 与 GEO
前端
IT_陈寒6 小时前
SpringBoot启动慢得像蜗牛?原来是这个配置在捣鬼
前端·人工智能·后端