文章目录

JavaScript Map.groupBy按条件分组的集合操作

发布于 2026-05-05 18:25:24 · 浏览 11 次 · 评论 0 条

JavaScript Map.groupBy按条件分组的集合操作

处理大量杂乱数据时,经常需要将具有相同特征的数据归纳到一起。传统的 for 循环或 reduce 方法代码冗长且难以阅读。Map.groupBy 提供了一种声明式、简洁的方式来完成这一任务,它可以根据你指定的回调函数规则,自动将数组元素归类到一个新的 Map 对象中。


1. 理解基本逻辑

在使用前,先明确 Map.groupBy 的核心运作流程。它接收一个可迭代对象(通常是数组)和一个回调函数,遍历每一项,根据回调函数返回的“键值”将数据放入对应的“桶”中。

以下是数据流转的示意图:

graph LR A["原始数组: [item1, item2, item3]"] --> B("Map.groupBy") B --> C{回调函数判定} C -->|"返回 Key A"| D["Map Key A"] C -->|"返回 Key B"| E["Map Key B"] C -->|"返回 Key A"| D D --> F["数组: [item1, item3]"] E --> G["数组: [item2]"]

2. 基础分组操作

以最简单的数字分组为例,将一组数字区分为“奇数”和“偶数”。

  1. 定义一个包含数字的数组。
  2. 调用 Map.groupBy 方法,传入数组和箭头函数。
  3. 编写判断逻辑:若数字除以 2 余数为 0,则标记为 even,否则为 odd
const numbers = [1, 2, 3, 4, 5, 6];

const result = Map.groupBy(numbers, (num) => {
  if (num % 2 === 0) {
    return 'even';
  } else {
    return 'odd';
  }
});

console.log(result);

执行上述代码后,你将得到一个 Map 对象。访问 result.get('odd') 将输出 [1, 3, 5]


3. 实战场景:商品列表分类

在实际开发中,更常见的场景是根据对象属性(如商品类别)进行分组。

假设有一组商品数据,结构如下:

属性名 说明
name 商品名称
category 商品分类

3.1 准备数据源

复制以下代码创建商品数组:

const products = [
  { name: '苹果', category: '水果' },
  { name: '胡萝卜', category: '蔬菜' },
  { name: '香蕉', category: '水果' },
  { name: '西兰花', category: '蔬菜' },
  { name: '牛肉', category: '肉类' }
];

3.2 执行按类别分组

使用对象解构语法提取 category 属性作为分组的键。

const groupedByCategory = Map.groupBy(products, ({ category }) => category);

// 验证结果
console.log(groupedByCategory.get('水果')); 
// 输出: [{ name: '苹果', category: '水果' }, { name: '香蕉', category: '水果' }]

4. 进阶:多条件与复杂逻辑分组

分组逻辑不仅限于单一属性,也可以是基于数值范围、字符串长度或组合逻辑。

4.1 按价格区间分组

假设 products 数组中的每个对象增加了 price 属性。我们要将商品按价格分为“便宜”(< 20)和“昂贵”(≥ 20)两组。

  1. 更新数据结构,添加价格信息。
  2. 修改回调函数逻辑,通过 price 判断返回的字符串。
const productsWithPrice = [
  { name: '苹果', price: 10 },
  { name: '牛肉', price: 50 },
  { name: '牛奶', price: 15 },
  { name: '龙虾', price: 80 }
];

const groupedByPrice = Map.groupBy(productsWithPrice, ({ price }) => {
  return price < 20 ? '便宜' : '昂贵';
});

console.log(groupedByPrice.get('昂贵')); 
// 输出: [{ name: '牛肉', price: 50 }, { name: '龙虾', price: 80 }]

4.2 使用对象作为分组键

Object.groupBy 不同,Map.groupBy 允许使用对象作为键,而不仅限于字符串。这在处理需要复杂标识符的场景时非常有用。

const key1 = { id: 1 };
const key2 = { id: 2 };

const items = [
  { name: 'Item A', groupKey: key1 },
  { name: 'Item B', groupKey: key2 },
  { name: 'Item C', groupKey: key1 }
];

const groupedByObject = Map.groupBy(items, ({ groupKey }) => groupKey);

// 使用对象 key1 获取数据
console.log(groupedByObject.get(key1)); 
// 输出: [{ name: 'Item A', groupKey: {...}}, { name: 'Item C', groupKey: {...}}]

5. 处理分组结果

Map.groupBy 返回的是一个 Map 实例。若需将其转换回普通数组或对象以便序列化传输(如发送给后端 API),需进行额外操作。

5.1 遍历 Map

使用 forEach 方法遍历分组结果。

groupedByCategory.forEach((items, category) => {
  console.log(`分类: ${category}`);
  console.log(`商品数量: ${items.length}`);
});

5.2 转换为普通对象

使用 Object.fromEntries 将 Map 转换为对象。

const objFormat = Object.fromEntries(groupedByCategory);
console.log(objFormat);
// 输出格式: { 水果: [...], 蔬菜: [...], 肉类: [...] }

6. 对比传统方案:reduce vs Map.groupBy

为了凸显 Map.groupBy 的优势,下表对比了传统 reduce 写法与新写法的差异。

特性 reduce 写法 Map.groupBy 写法
代码量 较多,需手动初始化 Map/对象 极少,一行代码搞定
可读性 较差,需深入理解累加器逻辑 极高,声明式,直观表达意图
维护性 容易在累加器逻辑中引入 Bug 逻辑内聚,不易出错
性能 相近 相近(底层实现类似)

以下是使用 reduce 实现相同功能的代码示例:

// 旧写法:冗长且容易出错
const groupedByReduce = products.reduce((acc, product) => {
  const key = product.category;
  if (!acc.has(key)) {
    acc.set(key, []);
  }
  acc.get(key).push(product);
  return acc;
}, new Map());

通过对比可以看出,Map.groupBy 省去了手动初始化容器、检查键是否存在以及手动添加元素的繁琐步骤。


7. 注意事项

  1. 浏览器兼容性:这是一个较新的特性。检查运行环境(如 Node.js 版本或用户浏览器版本)是否支持。如需支持旧环境,需引入 polyfill 或使用 reduce 降级处理。
  2. 引用相等:当使用对象作为分组键时,必须使用同一个对象的引用来取值,不能通过字面量 { id: 1 } 获取,因为它们在内存中地址不同。
  3. 非破坏性:该方法不会修改原始数组,而是返回新的 Map

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文