JS `Array Grouping` (提案) 的 `groupBy` 与 `groupToMap` 方法

咳咳,各位观众,各位大佬,晚上好!我是今天的主讲人,江湖人称“代码界的段子手”。今天咱们不聊风花雪月,就来唠唠 JavaScript 里呼声很高的新提案:Array Grouping,特别是里面的 groupBygroupToMap 这两位“分组小能手”。

说起分组,这事儿咱们在日常开发中肯定没少干。比如说,你有一堆用户数据,想按性别分成男女两拨;或者你有一堆订单数据,想按月份统计一下销售额。以前咋办?是不是得吭哧吭哧地写循环,写判断,写对象,一不小心还得踩几个坑?

现在好了,有了 groupBygroupToMap,这些烦恼统统扫光!它们就像两位武林高手,一个擅长快刀斩乱麻,一个擅长精雕细琢,都能帮你把数组数据安排得明明白白。

一、groupBy: 简洁明了,分组界的“大众情人”

先来说说 groupBy。这玩意儿的特点就是一个字:简单。它接收一个回调函数作为参数,这个回调函数决定了分组的依据。groupBy 会遍历数组中的每一个元素,把元素传给回调函数,回调函数返回的值就是这个元素所属的组的 key。最后,groupBy 会返回一个对象,这个对象的 key 就是分组的 key,value 就是属于这个组的元素组成的数组。

是不是有点绕?没关系,上代码,一看就懂:

const products = [
  { name: 'iPhone', category: 'Electronics', price: 999 },
  { name: 'Samsung Galaxy', category: 'Electronics', price: 899 },
  { name: 'T-shirt', category: 'Clothing', price: 20 },
  { name: 'Jeans', category: 'Clothing', price: 50 },
  { name: 'Book', category: 'Books', price: 15 }
];

const groupedProducts = products.groupBy(product => product.category);

console.log(groupedProducts);
/*
{
  Electronics: [
    { name: 'iPhone', category: 'Electronics', price: 999 },
    { name: 'Samsung Galaxy', category: 'Electronics', price: 899 }
  ],
  Clothing: [
    { name: 'T-shirt', category: 'Clothing', price: 20 },
    { name: 'Jeans', category: 'Clothing', price: 50 }
  ],
  Books: [
    { name: 'Book', category: 'Books', price: 15 }
  ]
}
*/

看,多简单!一行代码搞定分组! groupBy 就像个老好人,你给他一个分组的规则,他就老老实实地把数据分好,然后打包成一个对象给你。

用例一:按年龄段分组用户

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 32 },
  { name: 'Charlie', age: 18 },
  { name: 'David', age: 45 },
  { name: 'Eve', age: 22 }
];

const groupedUsers = users.groupBy(user => {
  if (user.age < 25) {
    return 'Young';
  } else if (user.age < 40) {
    return 'Mid-age';
  } else {
    return 'Old';
  }
});

console.log(groupedUsers);
/*
{
  Young: [
    { name: 'Alice', age: 25 },
    { name: 'Charlie', age: 18 },
    { name: 'Eve', age: 22 }
  ],
  'Mid-age': [ { name: 'Bob', age: 32 } ],
  Old: [ { name: 'David', age: 45 } ]
}
*/

用例二:按奇偶分组数字

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const groupedNumbers = numbers.groupBy(number => number % 2 === 0 ? 'Even' : 'Odd');

console.log(groupedNumbers);
/*
{
  Odd: [ 1, 3, 5, 7, 9 ],
  Even: [ 2, 4, 6, 8, 10 ]
}
*/

怎么样,是不是感觉 groupBy 特别亲民?它适用于大多数简单的分组场景,让你告别繁琐的循环和判断。

二、groupToMap: 精益求精,分组界的“技术大牛”

接下来,咱们聊聊 groupToMap。和 groupBy 相比,groupToMap 就像一位精益求精的技术大牛。它也接收一个回调函数作为参数,这个回调函数同样决定了分组的依据。但是,groupToMap 返回的不是一个对象,而是一个 Map 对象。

Map 对象有什么好处呢?

  • Key 的类型更自由: 对象的 key 只能是字符串或 Symbol,而 Map 的 key 可以是任意类型,包括对象、函数等等。
  • 顺序性: Map 对象会保留 key 的插入顺序,而对象的 key 是无序的(虽然现在大多数浏览器会按照插入顺序遍历对象的 key,但这并不是规范要求的)。
  • 性能: 在某些场景下,Map 对象的性能可能比普通对象更好,尤其是在 key 的数量非常大的时候。

总的来说,groupToMap 更加灵活,也更适合一些特殊的场景。

还是上代码:

const products = [
  { name: 'iPhone', category: 'Electronics', price: 999 },
  { name: 'Samsung Galaxy', category: 'Electronics', price: 899 },
  { name: 'T-shirt', category: 'Clothing', price: 20 },
  { name: 'Jeans', category: 'Clothing', price: 50 },
  { name: 'Book', category: 'Books', price: 15 }
];

const groupedProducts = products.groupToMap(product => product.category);

console.log(groupedProducts);
/*
Map(3) {
  'Electronics' => [
    { name: 'iPhone', category: 'Electronics', price: 999 },
    { name: 'Samsung Galaxy', category: 'Electronics', price: 899 }
  ],
  'Clothing' => [
    { name: 'T-shirt', category: 'Clothing', price: 20 },
    { name: 'Jeans', category: 'Clothing', price: 50 }
  ],
  'Books' => [ { name: 'Book', category: 'Books', price: 15 } ]
}
*/

可以看到,groupToMap 返回的是一个 Map 对象,它的 key 是分类名称,value 是属于该分类的商品组成的数组。

用例一:使用对象作为 Key 进行分组

假设我们有一个包含学生信息的数组,每个学生都有一个 grade 属性,表示年级。我们想按照年级进行分组,但是年级本身是一个对象,包含 namelevel 两个属性。

const students = [
  { name: 'Alice', grade: { name: '一年级', level: 1 } },
  { name: 'Bob', grade: { name: '二年级', level: 2 } },
  { name: 'Charlie', grade: { name: '一年级', level: 1 } },
  { name: 'David', grade: { name: '三年级', level: 3 } },
  { name: 'Eve', grade: { name: '二年级', level: 2 } }
];

const groupedStudents = students.groupToMap(student => student.grade);

console.log(groupedStudents);
/*
Map(3) {
  { name: '一年级', level: 1 } => [
    { name: 'Alice', grade: { name: '一年级', level: 1 } },
    { name: 'Charlie', grade: { name: '一年级', level: 1 } }
  ],
  { name: '二年级', level: 2 } => [
    { name: 'Bob', grade: { name: '二年级', level: 2 } },
    { name: 'Eve', grade: { name: '二年级', level: 2 } }
  ],
  { name: '三年级', level: 3 } => [
    { name: 'David', grade: { name: '三年级', level: 3 } }
  ]
}
*/

在这个例子中,我们使用 grade 对象作为 Map 的 key,这样就可以方便地按照年级进行分组了。

用例二:需要保留插入顺序的分组

假设我们有一个包含日志信息的数组,每条日志都有一个 timestamp 属性,表示日志的创建时间。我们想按照小时进行分组,并且需要保留日志的插入顺序。

const logs = [
  { message: 'Log 1', timestamp: new Date('2023-10-27T10:30:00') },
  { message: 'Log 2', timestamp: new Date('2023-10-27T10:45:00') },
  { message: 'Log 3', timestamp: new Date('2023-10-27T11:00:00') },
  { message: 'Log 4', timestamp: new Date('2023-10-27T11:15:00') },
  { message: 'Log 5', timestamp: new Date('2023-10-27T10:15:00') }
];

const groupedLogs = logs.groupToMap(log => log.timestamp.getHours());

console.log(groupedLogs);
/*
Map(2) {
  10 => [
    { message: 'Log 1', timestamp: 2023-10-27T02:30:00.000Z },
    { message: 'Log 2', timestamp: 2023-10-27T02:45:00.000Z },
    { message: 'Log 5', timestamp: 2023-10-27T02:15:00.000Z }
  ],
  11 => [
    { message: 'Log 3', timestamp: 2023-10-27T03:00:00.000Z },
    { message: 'Log 4', timestamp: 2023-10-27T03:15:00.000Z }
  ]
}
*/

在这个例子中,我们使用 groupToMap 来按照小时进行分组,并且 Map 对象会保留日志的插入顺序,这样我们就可以按照时间顺序来查看每个小时的日志信息了。注意 Map 只会保持key的插入顺序,组内的元素顺序还是原始顺序。

三、groupBy vs groupToMap: 如何选择?

那么,在实际开发中,我们应该选择 groupBy 还是 groupToMap 呢? 别急,我给你总结了一个表格,让你一目了然:

特性 groupBy groupToMap
返回值类型 普通对象 Map 对象
Key 的类型 只能是字符串或 Symbol 可以是任意类型
顺序性 无序(虽然现在大多数浏览器会按照插入顺序遍历) 保留 key 的插入顺序
性能 一般情况下足够用 在 key 的数量非常大的时候可能更好
适用场景 简单分组,对 key 的类型和顺序没有特殊要求 需要使用对象作为 key,或者需要保留插入顺序

总的来说,如果你的分组需求比较简单,对 key 的类型和顺序没有特殊要求,那么 groupBy 就能满足你的需求。如果你的分组需求比较复杂,需要使用对象作为 key,或者需要保留插入顺序,那么 groupToMap 才是你的最佳选择。

四、兼容性考虑

需要注意的是,groupBygroupToMap 目前还是提案阶段,还没有被所有浏览器原生支持。如果你想在现有的项目中使用它们,可以使用 polyfill 或者使用一些第三方库来实现类似的功能。

例如,你可以使用 core-js 来提供 polyfill:

npm install core-js

然后在你的代码中引入 polyfill:

import 'core-js/features/array/group';
import 'core-js/features/array/group-to-map';

// 现在你就可以使用 groupBy 和 groupToMap 了

五、总结

好了,今天的讲座就到这里了。希望通过今天的讲解,大家对 JavaScript 的 groupBygroupToMap 有了更深入的了解。记住,它们就像两位分组界的“武林高手”,一个简洁明了,一个精益求精,都能帮你轻松搞定数组分组!

记住,写代码就像写段子,要简洁,要幽默,要让人一看就懂! 咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注