
本文介绍如何用一次 reduce 操作替代多次 filter + reduce,将方向数组(如 ['n','s',...'])高效统计并写入对象,避免重复遍历,提升可读性与性能。
本文介绍如何用一次 reduce 操作替代多次 filter + reduce,将方向数组(如 ['n','s',...'])高效统计并写入对象,避免重复遍历,提升可读性与性能。
在处理方向移动数据(如 'n' 表示北、's' 表示南)时,目标是统计每种方向出现的频次,并以键值对形式存入对象(例如 { n: 5, s: 5 })。原代码通过两次 filter 分别筛选 'n' 和 's',再各自调用 reduce 累加计数——这不仅导致数组被遍历两次,还重复创建中间数组,违背函数式编程中“单一数据流”的简洁原则。
更优解是仅遍历一次数组,用 reduce 直接构建计数对象:
const walk = ['n', 's', 'n', 's', 'n', 's', 'n', 's', 'n', 's'];
const result = walk.reduce((acc, direction) => {
acc[direction] = (acc[direction] || 0) + 1;
return acc;
}, {});
console.log(result); // { n: 5, s: 5 }该写法核心在于:
- 初始累加器 acc 为一个空对象 {};
- 每次迭代中,以当前方向 direction 为键,安全读取已有计数(acc[direction] || 0),加 1 后重新赋值;
- || 0 保证首次访问未定义键时默认从 0 开始计数,无需预初始化。
✅ 优势总结:
- 时间复杂度从 O(2n) 降至 O(n),空间上避免生成两个中间过滤数组;
- 逻辑内聚,扩展性强——若后续增加 'e'(东)、'w'(西)等方向,无需新增代码块,自动支持;
- 符合 reduce 的本意:将数组“折叠”为单一聚合值(此处即计数对象)。
⚠️ 注意事项:
- 若需将 'n' 映射为语义化键名(如 "north"),可在累加时做映射:
const dirMap = { n: 'north', s: 'south', e: 'east', w: 'west' }; acc[dirMap[direction]] = (acc[dirMap[direction]] || 0) + 1; - 不建议在 reduce 中直接修改外部对象(如题中 object),应始终返回新状态,保持纯函数特性。
此模式适用于任何分类计数场景(如统计用户操作类型、日志事件频次等),是 reduce 的典型高价值用例。


















