本文详解如何利用 array.prototype.reduce 高阶函数高效统计数组中每个元素的出现频次,生成键值对形式的计数对象,并附带可运行示例、边界注意事项与优化建议。
本文详解如何利用 array.prototype.reduce 高阶函数高效统计数组中每个元素的出现频次,生成键值对形式的计数对象,并附带可运行示例、边界注意事项与优化建议。
在 JavaScript 中,reduce 是一个功能强大且富有表现力的数组归约方法,特别适合将数组“压缩”为单一聚合值(如总和、最大值或——正如本文所聚焦的——频次映射表)。相比传统 for 循环,reduce 更具函数式编程风格,逻辑内聚、无副作用(若正确使用),且天然支持链式操作。
以下是一个清晰、健壮的实现方式:
const array = [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 1, 2, 3, 1, 2, 3, 3, 3, 3, 3, 4, 98, -546, 4];
const frequencyMap = array.reduce((acc, curr) => {
// 确保当前元素作为键存在,初始计数为 0(若未定义)
acc[curr] = (acc[curr] || 0) + 1;
return acc;
}, {}); // 初始累加器为一个空对象
console.log(frequencyMap);
// 输出:
// {
// '1': 4, '2': 4, '3': 8, '4': 3, '5': 1,
// '6': 1, '7': 1, '98': 1, '-546': 1
// }✅ 关键点解析:
- acc(accumulator)是每次迭代累积的结果对象,初始值为 {};
- curr(current value)是当前遍历的数组元素;
- acc[curr] = (acc[curr] || 0) + 1 是核心逻辑:利用逻辑或 || 提供默认值,避免 undefined + 1 导致 NaN,简洁安全;
- 每次必须显式返回 acc,否则下一次迭代的 acc 将为 undefined,导致运行时错误。
⚠️ 注意事项:
立即学习“Java免费学习笔记(深入)”;
-
类型隐式转换:对象键始终为字符串(如 -546 会变为 '-546',true 变为 'true')。若需严格区分原始类型(如 0 与 '0'),应改用 Map:
const map = array.reduce((acc, curr) => { acc.set(curr, (acc.get(curr) || 0) + 1); return acc; }, new Map()); -
不可变性考量:上述写法直接修改 acc 对象(即可变归约),虽高效且符合常规实践,但在强调不可变性的场景(如 Redux 或某些函数式库)中,可改用展开语法(性能略低,适用于小数组):
array.reduce((acc, curr) => ({ ...acc, [curr]: (acc[curr] || 0) + 1 }), {}); - 空数组安全:reduce 在空数组上调用且未提供初始值会抛错;但此处已显式传入 {},故完全安全。
? 进阶提示:若只需统计某一个特定元素(如仅查 3 出现几次),可进一步简化为:
const countOfThree = array.reduce((count, num) => num === 3 ? count + 1 : count, 0); // → 8
总结而言,reduce 统计频次的核心在于合理初始化累加器、安全更新键值、并始终返回累加器。掌握这一模式,不仅能解决计数问题,更能迁移到分组、嵌套聚合、状态机构建等更复杂的数据处理场景。










