
本文介绍如何高效统计数组中以id为唯一标识的对象出现次数,并按id从高到低排序,最终生成如“2x banana, 1x apple, 2x strawberry”格式的可读字符串。
本文介绍如何高效统计数组中以id为唯一标识的对象出现次数,并按id从高到低排序,最终生成如“2x banana, 1x apple, 2x strawberry”格式的可读字符串。
在处理类似购物车的数据结构时,常需将具有相同 id 的多个对象合并统计(而非依赖 name 或其他字段),并输出用户友好的汇总字符串。核心挑战在于:去重逻辑必须基于 id 字段;计数需准确;结果需按 id 降序排列;名称需首字母大写且格式统一。
以下是推荐的三步函数式实现方案:
✅ 步骤解析与代码实现
排序(ID 降序)
使用 Array.prototype.toSorted()(ES2024)保持原数组不变,按 id 从大到小排序。若需兼容旧环境,可用 [...data].sort((a, b) => b.id - a.id) 替代。聚合统计(Map → Object)
利用 reduce() 构建以 id 为键的对象字典,首次遇到某 id 时记录其 name(保证名称一致性),每次出现则 count++。使用空值合并操作符 ??= 简洁初始化。格式化输出
对 Object.values() 结果映射为 "Nx Name" 字符串,其中 capitalise() 辅助函数确保单词首字母大写(如 "banana" → "Banana"),最后用 join(", ") 拼接。
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 对应不同名称),本方案仅保留首次出现的 name,避免歧义。如需校验一致性,可在 reduce 中添加断言:if (acc[id] && acc[id].name !== name) throw new Error(...)。
- 性能考量:时间复杂度为 O(n log n)(主因是排序),对万级以内数据表现优异;若原始数据已按 ID 有序,可跳过排序步骤,直接 reduce 后手动按 id 排序 Object.entries()。
- 兼容性兜底:toSorted() 在 Node.js ≥20.1 和现代浏览器中支持良好;若需支持 IE 或旧版 Node,请替换为 [...data].sort((a, b) => b.id - a.id)。
该方法兼顾可读性、健壮性与函数式编程原则,无需临时 Map 或双重遍历,是处理此类聚合展示需求的推荐实践。

















