用 reduce 做数据透视的核心是将分组与聚合压缩进累加器:先动态生成唯一字符串 key(如 ${city}-${category}),再按需设计累加器结构(如 {sum: 0, count: 0}),最后安全更新并后处理。

用 reduce 做数据透视,核心是把“按维度分组 + 聚合计算”这两步,压缩进一个累加器对象里。关键不在写法多炫,而在想清楚:分组键怎么生成?聚合值怎么更新?初始值设成什么?
明确分组维度,动态构造唯一 key
不能硬编码字段名,得根据需求拼出可区分的分组标识。比如按城市+品类统计销售额,key 就是 `${city}-${category}`;如果多级分组(如省-市-区),就用数组 join 或嵌套对象结构。
常见错误是直接用对象当 key(JS 会自动转成 [object Object]),必须转成字符串或用 Map 存储嵌套结构。
- 简单组合:`key = item.city + '|' + item.category`
- 安全序列化(含可能为 null 的字段):`key = JSON.stringify([item.region, item.productType])`
- 多维嵌套(避免 key 冲突):用 `Map` 或 `Object` 层层代理,例如 `acc[region] = acc[region] || {}; acc[region][city] = ...`
设计累加器结构,匹配最终输出格式
reduce 的初始值(第二个参数)要和你想要的透视结果结构一致。如果是“城市 → 各品类销售额总和”,初始值就是空对象 {};如果还要带计数、平均值等,累加器每个分组项就得是对象,包含 sum、count、avg 等字段。
别在 reduce 里反复计算平均值——先累计 sum 和 count,最后再算 avg,更准确也更高效。
- 单指标汇总:
{},值为数字 - 多指标汇总:
{},值为{ sum: 0, count: 0, max: -Infinity } - 保留明细数组:
{},值为[],用 push 收集原始项
在 reducer 函数里安全更新聚合值
每次迭代,先算出当前项的分组 key,再检查累加器里是否存在该 key 对应的聚合单元。不存在就初始化,存在就更新。注意数值类聚合(sum、max)要判空/类型,避免 NaN。
示例场景:统计每种商品在各仓库的库存总量与最晚入库时间
- key =
`${item.warehouseId}-${item.sku}` - 初始化:
acc[key] = { total: 0, latestDate: null } - 更新:
acc[key].total += Number(item.qty) || 0;if (!acc[key].latestDate || item.date > acc[key].latestDate) acc[key].latestDate = item.date
后处理:扁平化、排序、补全缺失维度
reduce 输出常是嵌套或稀疏结构,真实业务中往往需要转成数组、按某字段排序、或补上没有数据的组合(如某城市没卖过某品类,也要显示 0)。这些不要塞进 reduce 里,单独做更清晰。
- 转数组:
Object.entries(acc).map(([k, v]) => ({ key: k, ...v })) - 补全维度:先生成所有合法组合(笛卡尔积),再用 reduce 结果 merge,默认值为 0
- 排序:用
Array.prototype.sort(),例如按销售额倒序:.sort((a, b) => b.sum - a.sum)
不复杂但容易忽略:reduce 是纯函数,别在回调里改原始数据;key 生成逻辑要稳定;初始值类型必须和后续赋值类型一致。写完跑一两个边界 case(空数组、null 字段、重复 key)就能避开大半坑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











