对数组中的我的项目进行分组,你可能曾经做过很屡次了。每次都会手动编写一个分组函数,或者应用 lodash
的 groupBy
函数。
好消息是,JavaScript 当初有了分组办法,所以你再也不用这样做了。Object.groupBy
和 Map.groupBy
这两个新办法将使分组变得更简略,并节俭咱们的工夫或依赖性。
以前的做法
假如你有一个代表人的对象数组,你想按年龄对它们进行分组。你能够这样应用 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
的任何属性。这很好,因为这意味着你不会意外笼罩 Object.prototype
上的任何属性,但这也意味着该对象没有你可能冀望的任何办法,如 hasOwnProperty
或 toString
。
const peopleByAge = Object.groupBy(people, (person) => person.age);
console.log(peopleByAge.hasOwnProperty("28"));
// TypeError: peopleByAge.hasOwnProperty is not a function
传递给 Object.groupBy
的回调函数应返回字符串或 Symbol
。如果返回其余内容,则将强制转为字符串。
在咱们的示例中,咱们始终以数字模式返回 age
,但在后果中却被强制转为字符串。尽管如此,你依然能够应用数字拜访属性,因为应用方括号符号也会将参数强制为字符串。
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
之外,Map.groupBy
的性能与 Object.groupBy
简直雷同。这意味着你能够应用所有罕用的 Map
函数。这也意味着你能够从回调函数返回任何类型的值。
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 中按对象检索我的项目,对象必须具备雷同的援用。
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 提议的一部分,目前处于第三阶段。这意味着它很有可能成为一项规范,因而也呈现了一些实施方案。
Chrome 浏览器 117 版本刚刚推出了对这两种办法的反对,而 Firefox 浏览器 119 版本也公布了对这两种办法的反对。Safari 以不同的名称实现了这些办法,我置信他们很快就会更新。既然 Chrome 浏览器中呈现了这些办法,就意味着它们已在 V8 中实现,因而下次 V8 更新时,Node 中也会呈现这些办法。
为什么应用静态方法
你可能会问,为什么要以 Object.groupBy
而不是 Array.prototype.groupBy
的模式来实现呢?依据该提案,有一个库已经用一个不兼容的 groupBy
办法对 Array.prototype
进行了猴子补丁。在思考新的利用程序接口时,向后兼容性十分重要。几年前,在尝试实现 Array.prototype.flatten
时,这一点在一次被称为 SmooshGate 的事件中失去了强调。
侥幸的是,应用静态方法仿佛更有利于将来的可扩展性。当 Record 和 Tuples 提议实现时,咱们能够增加一个 Record.groupB
y 办法,用于将数组分组为不可变的记录。
总结
将我的项目分组显然是咱们开发人员的一项重要工作。目前,每周从 npm 下载 lodash.groupBy
的次数在 150 万到 200 万之间。很快乐看到 JavaScript 填补了这些空白,让咱们的工作变得更加轻松。
当初,下载 Chrome 117 并亲自尝试这些新办法吧。