JavaScript 中的数组分组(2024)
数组分组是您可能在 JavaScript 中实现过的任务。如果您使用 SQL,它类似于执行GROUP BY
。给定一个数据集,我们可以通过将相似的数据放入一个组中,并使用给定的标识符标识每个组来组成更高级别的数据集。
我将深入研究今年(2024 年)发布的新数组分组Object.groupBy
函数,即和Map.groupBy
。
对于我的 TypeScript 粉丝来说, TypeScript 5.4版本已经支持这些 API 。不过,你需要将你的tsconfig.json
目标配置为ESNext
。当它们在 ES2024 中可用时,你可以将目标设置为ES2024
或更高版本。
2024 年之前的阵列分组
JavaScript 中的数组分组并非新概念,我们已经在各种方法中实现了它。它可以通过underscore.js或lodashfor
中的或foreach
循环Array.prototype.reduce
、或groupBy函数来实现。
给定一个员工列表,我们将使用以下方式对数据进行分组reduce()
:
interface Employee {
name: string;
department: string;
age: number;
manager?: Employee;
joined: Date
}
const ceo = {
name: "John Doe",
department: "engineering",
age: 47,
joined: new Date("10-04-2020")
}
const cfo = {
name: "Anna Maria",
department: "finance",
age: 45,
joined: new Date("10-05-2020")
}
const employees: Employee[] = [
ceo,
{
name: "Pop Jones Jr.",
department: "finance",
age: 30,
manager: cfo,
joined: new Date("10-04-2021")
},
{
name: "Sarah Clara",
department: "engineering",
age: 32,
manager: ceo,
joined: new Date("10-05-2021")
},
cfo,
{
name: "Funmi Kola",
department: "engineering",
age: 20,
manager: ceo,
joined: new Date("10-05-2022")
},
{
name: "Julius Maria",
department: "sales",
age: 27,
manager: cfo,
joined: new Date("10-05-2022")
}
]
const groupByDepartment = employees.reduce<Record<string, Employee[]>>((acc, employee) => {
const department = employee.department;
if (acc[department] === undefined) {
acc[department] = [];
}
acc[department].push(employee);
return acc;
}, {});
console.log(groupByDepartment);
的输出groupByDepartment
应该是:
{
"engineering": [
{
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
{
"name": "Sarah Clara",
"department": "engineering",
"age": 32,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2021-10-04T22:00:00.000Z"
},
{
"name": "Funmi Kola",
"department": "engineering",
"age": 20,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}
],
"finance": [
{
"name": "Pop Jones Jr.",
"department": "finance",
"age": 30,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2021-10-03T22:00:00.000Z"
},
{
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
}
],
"sales": [
{
"name": "Julius Maria",
"department": "sales",
"age": 27,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}
]
}
使用 Object.groupBy 进行分组
该Object.groupBy
函数用于按给定的字符串值进行分组。这意味着:给定一个可迭代对象,根据其提供的回调函数返回的值对其元素进行分组。回调函数的返回值必须是可以强制转换为字符串或符号的值。
Object.groupBy
返回一个空原型对象,该对象为每个组提供单独的属性,其中包含包含该组元素的数组。使用空原型对象可以实现符合人体工程学的解构,并防止与全局 Object 属性发生意外冲突。
使用相同的数据集,我们可以根据员工的部门对其进行分组Object.groupBy
:
const groupByDepartment = Object.groupBy(employees, ({department}) => department)
这给出了与方法相同的结果,reduce
但代码更少。 的输出groupByDepartment
应该是:
{
"engineering": [
{
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
{
"name": "Sarah Clara",
"department": "engineering",
"age": 32,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2021-10-04T22:00:00.000Z"
},
{
"name": "Funmi Kola",
"department": "engineering",
"age": 20,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}
],
"finance": [
{
"name": "Pop Jones Jr.",
"department": "finance",
"age": 30,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2021-10-03T22:00:00.000Z"
},
{
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
}
],
"sales": [
{
"name": "Julius Maria",
"department": "sales",
"age": 27,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}
]
}
使用 Map.groupBy 进行分组
Map.groupBy
与 类似Object.groupBy
,不同之处在于它返回一个 Map,并且可以使用任意值对给定的可迭代对象进行分组。在本例中,可以使用对象对数据集进行分组。
回顾数据集,我们manager
为一些员工定义了一个属性,并为其分配了ceo
或cfo
对象。我们可以根据员工的 对其进行分组manager
,如下所示:
const managerWithTeammates = Map.groupBy(employees, ({manager}) => manager)
的输出managerWithTeammates
应类似于:
Map (3)
{undefined => [{
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
}, {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
}],
{
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
} => [{
"name": "Pop Jones Jr.",
"department": "finance",
"age": 30,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2021-10-03T22:00:00.000Z"
}, {
"name": "Julius Maria",
"department": "sales",
"age": 27,
"manager": {
"name": "Anna Maria",
"department": "finance",
"age": 45,
"joined": "2020-10-04T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}],
{
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
} => [{
"name": "Sarah Clara",
"department": "engineering",
"age": 32,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2021-10-04T22:00:00.000Z"
}, {
"name": "Funmi Kola",
"department": "engineering",
"age": 20,
"manager": {
"name": "John Doe",
"department": "engineering",
"age": 47,
"joined": "2020-10-03T22:00:00.000Z"
},
"joined": "2022-10-04T22:00:00.000Z"
}]}
在这个例子中,我们有 3 个组:
-
没有经理的CEO和CFO,以
undefined
作为Map对象键。 -
首席财务官 Anna Maria 手下有两名员工。这使用
cfo
对象作为 Map 对象的键。 -
首席执行官 John Doe 手下有两名员工。这将使用该
ceo
对象作为 Map 对象的键。
结论
JavaScript 即将推出的新 API 是不是令人惊叹?Object.groupBy
andMap.groupBy
函数让数组或可迭代对象中的数据分组变得更容易,而且比传统方法更符合人体工程学。此外,它们不需要像or 这样的reduce
外部库,从而减少了包的大小。lodash
underscore.js
总结一下,该Map.groupBy
函数主要用于对与任意对象关联的元素进行分组,尤其是当该对象可能随时间变化时。如果对象是不变的,您可以改用字符串表示,并使用 对元素进行分组Object.groupBy()
。如果使用 TypeScript,请将目标设置为ESNext
(或 ,ES2024
当它在未来可用时)。
如果您有任何问题或建议,请随时发表评论。您也可以通过Twitter联系我。感谢您的阅读😎
鏂囩珷鏉ユ簮锛�https://dev.to/pmbanugo/array-grouping-in-javascript-2024-5cm9您将通过此链接找到一个包含此博客文章中使用的代码的游乐场。
如果您对类似主题但视频格式感兴趣,请订阅我的YouTube 频道:www.youtube.com/@pmbanugo
最初由我撰写并发布在Telerik 的博客上