
本文介绍如何用一次 reduce 操作高效统计字符串数组中每个方向(如 'n'、's')的出现频次,并直接汇总为键值对对象,避免重复遍历和冗余 filter。
本文介绍如何用一次 reduce 操作高效统计字符串数组中每个方向(如 'n'、's')的出现频次,并直接汇总为键值对对象,避免重复遍历和冗余 filter。
在处理方向路径(如 ['n','s','n','s'])时,目标通常是统计每种移动方向的总次数,并以对象形式组织结果(例如 { n: 5, s: 5 })。原始代码通过两次 filter + reduce 分别统计 'n' 和 's',不仅造成数组重复遍历(时间复杂度 O(2n)),还引入了不必要的中间数组和状态耦合(如 object["north"] 的硬编码键名与原始数据 'n' 不一致),降低了可维护性与扩展性。
更优解是单次遍历 + 动态键名聚合:利用 reduce 的累加器(初始为空对象 {})作为计数容器,将每个元素(如 'n')直接作为对象属性名,通过 (r[direction] || 0) + 1 实现安全自增——若该键未定义则取 0,否则取当前值再加 1。这种方式时间复杂度降至 O(n),且天然支持任意方向(如新增 'e' 或 'w' 无需修改逻辑)。
以下是优化后的完整示例:
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 是唯一被修改的对象,不要在回调中创建新对象(如 return { ...acc, [direction]: ... }),否则会显著降低性能;
- 键名语义一致性:若业务要求键名为 "north" 而非 "n",可在累加前映射:const keyMap = { n: 'north', s: 'south' }; acc[keyMap[direction]] = ...;
-
类型安全补充(可选):结合 TypeScript 可定义 Record
类型约束累加器结构。
总结:reduce 的核心优势在于状态聚合能力——它并非仅适用于数值计算,更是构建结构化统计对象的理想工具。善用动态属性访问与逻辑或默认值(|| 0),即可用简洁、通用、高性能的方式替代多轮过滤与手动赋值。











