
本文详解如何使用 JavaScript 的 reduce() 等原生方法,对具有重复键(如 name/date/img/cost)的对象数组进行智能分组,并聚合特定字段(如 paid 求和)、收集关联数据(如 username 归集为数组)。
本文详解如何使用 javascript 的 `reduce()` 等原生方法,对具有重复键(如 name/date/img/cost)的对象数组进行智能分组,并聚合特定字段(如 `paid` 求和)、收集关联数据(如 `username` 归集为数组)。
在实际开发中,后端返回的扁平化列表(如多个用户购买同一商品的记录)常需前端聚合处理:相同商品项应合并为一条,同时累计支付金额、归集用户名等。这类需求本质是按多字段分组 + 多维度聚合,不能简单用 flat() 或 concat() 解决,而需结合 reduce() 与精确匹配逻辑。
以下以真实业务场景为例:原始数据包含 name、date、img、cost、paid 和 username 字段,目标是将所有字段完全一致的商品项合并,同时:
- 对
paid字段求和; - 将
username提取并存入嵌套数组usernames: [{ username: "xxx" }]; - 保留首次出现的其他字段值(因
name/date/img/cost已作为分组依据,理论上恒等)。
✅ 推荐实现方案(健壮、可读、可扩展)
const array = [
{ name: 'Iphone', date: '01.01.2024', img: 'img/iphone.png', cost: 2500, paid: 500, username: "Joe" },
{ name: 'Samsung', date: '01.01.2024', img: 'img/samsung.png', cost: 2000, paid: 200, username: "Adam" },
{ name: 'Samsung', date: '01.01.2024', img: 'img/samsung.png', cost: 2000, paid: 100, username: "Alvin" }
];
const grouped = array.reduce((acc, curr) => {
// 定义分组键:所有用于判断“是否为同一商品项”的字段
const key = `${curr.name}|${curr.date}|${curr.img}|${curr.cost}`;
const existing = acc.find(item => item._key === key);
if (existing) {
// 已存在:累加 paid,追加 username
existing.paid += curr.paid;
existing.usernames.push({ username: curr.username });
} else {
// 首次出现:初始化分组对象,添加 _key 便于后续查找(也可用 Map 替代)
acc.push({
_key: key,
name: curr.name,
date: curr.date,
img: curr.img,
cost: curr.cost,
paid: curr.paid,
usernames: [{ username: curr.username }]
});
}
return acc;
}, []).map(({ _key, ...rest }) => rest); // 移除临时 _key 字段
console.log(grouped);
// 输出:
// [
// { name: 'Iphone', date: '01.01.2024', img: 'img/iphone.png', cost: 2500, paid: 500, usernames: [{ username: 'Joe' }] },
// { name: 'Samsung', date: '01.01.2024', img: 'img/samsung.png', cost: 2000, paid: 300, usernames: [{ username: 'Adam' }, { username: 'Alvin' }] }
// ]
? 关键设计说明
-
分组键生成:使用字符串拼接(
name|date|img|cost)构建唯一键,比多次&&判断更简洁、易维护;若字段含|需改用JSON.stringify({name, date, img, cost})。 -
聚合灵活性:
paid += curr.paid可替换为任意聚合逻辑(如取最大值Math.max(existing.paid, curr.paid))。 -
性能考量:
find()在大数据量时复杂度为 O(n²),如需优化,推荐改用Map:const map = new Map(); array.forEach(curr => { const key = `${curr.name}|${curr.date}|${curr.img}|${curr.cost}`; if (map.has(key)) { const item = map.get(key); item.paid += curr.paid; item.usernames.push({ username: curr.username }); } else { map.set(key, { name: curr.name, date: curr.date, img: curr.img, cost: curr.cost, paid: curr.paid, usernames: [{ username: curr.username }] }); } }); const grouped = [...map.values()];
⚠️ 注意事项
- 原始数据中
paid字段必须存在且为数字类型,否则累加会触发隐式类型转换(如"200" + 100 → "200100"),建议提前校验:typeof curr.paid === 'number' && !isNaN(curr.paid)。 - 若分组依据字段可能为
null/undefined,字符串拼接前需做空值处理(如curr.name ?? ''),避免生成不一致的 key。 - 本方案默认保留首次出现的
name/date/img/cost,若这些字段在同组内实际存在差异,需明确业务规则(如报错、取最新值等)。
掌握此模式后,你可轻松应对类似需求:合并订单明细、聚合用户行为日志、统计多维度报表数据等——核心始终是「定义分组逻辑」+「声明聚合操作」。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











