
本文介绍一种高效、可读性强的方法:对含重复 ID 的对象数组按 ID 降序排序,聚合统计各 ID 出现次数及对应名称,并生成如 "2x Banana, 1x Apple, 2x Strawberry" 的规范化字符串。
本文介绍一种高效、可读性强的方法:对含重复 id 的对象数组按 id 降序排序,聚合统计各 id 出现次数及对应名称,并生成如 `"2x banana, 1x apple, 2x strawberry"` 的规范化字符串。
在电商、购物车或库存类场景中,常需将多个相同 ID 的商品对象(如多条“香蕉”记录)合并为一条带数量的描述。关键在于:唯一性由 id 决定,而非 name;统计需保留首次出现的 name 值;最终字符串须按 ID 从高到低排序。
以下是推荐实现方案,分三步完成:
✅ 步骤一:按 ID 降序排序
使用 Array.prototype.toSorted()(不修改原数组)确保后续聚合顺序正确。若需兼容旧环境(如 Node.js b.id - a.id) 替代。
✅ 步骤二:用 reduce 聚合统计
初始化空对象 acc,遍历排序后数组:
- 若 acc[id] 不存在,用当前对象的 name 和初始计数 0 初始化;
- 每次命中同一 id 时,count++;
- 利用 ??=(空值合并赋值)简洁处理首次初始化逻辑。
✅ 步骤三:格式化并拼接字符串
对 Object.values(counts) 映射为 "${count}x ${CapitalisedName}",再以 ", 连接。
完整可运行代码如下:
const data = [
{ "id": 5, "name": "banana" },
{ "id": 5, "name": "banana" },
{ "id": 4, "name": "apple" },
{ "id": 1, "name": "strawberry" },
{ "id": 1, "name": "strawberry" }
];
const capitalise = (str) => str.replace(/\b[a-z]/g, (c) => c.toUpperCase());
const counts = data
.toSorted((a, b) => b.id - a.id)
.reduce((acc, { id, name }) => {
acc[id] ??= { name, count: 0 };
acc[id].count++;
return acc;
}, {});
const result = Object.values(counts)
.map(({ name, count }) => `${count}x ${capitalise(name)}`)
.join(", ");
console.log(result); // "2x Banana, 1x Apple, 2x Strawberry"
⚠️ 注意事项:
- 名称冲突处理:本方案采用「首次出现优先」策略(即相同 id 下,仅保留第一个 name),符合业务中“ID 唯一标识商品”的前提;
- 大小写规范:capitalise() 使用正则匹配单词首字母并转大写,稳健支持多词名称(如 "green apple" → "Green Apple");
- 性能友好:单次遍历 + 哈希查找,时间复杂度 O(n log n)(主要由排序决定),空间复杂度 O(k),k 为唯一 ID 数量;
- 兼容性兜底:生产环境若需支持 toSorted() 不可用的平台,建议封装为工具函数,例如:
const safeSortByIdDesc = (arr) => [...arr].sort((a, b) => b.id - a.id);
该方法结构清晰、语义明确,兼顾可维护性与执行效率,是处理此类“分组计数+格式化输出”需求的推荐实践。











