可用reduce模拟SQL聚合:以分组字段为key构建对象,累加数值并维护count,最后计算avg;支持动态字段、空值处理及多字段联合分组,代码简洁高效。

可以用 JavaScript 数组的 reduce 配合对象暂存,直接模拟 SQL 的 GROUP BY + SUM/AVG/COUNT 等基础聚合逻辑,无需引入额外库,代码简洁且可读性强。
用 reduce 实现分组求和(类似 GROUP BY + SUM)
核心思路是:遍历数组,以分组字段为 key 构建中间对象,对每个目标数值字段做累加。
- 初始化一个空对象作为 accumulator
- 每次迭代时,取出分组键(如
item.ProjectType)和待聚合值(如item.Amount) - 若该 key 尚未存在,就创建新条目并初始化数值字段为 0;否则直接累加
- 最后用
Object.values()提取结果数组
示例:
const result = frames.reduce((acc, item) => {const key = item.ProjectType;
if (!acc[key]) acc[key] = { ProjectType: key, Amount: 0, Hours: 0 };
acc[key].Amount += item.Amount;
acc[key].Hours += item.Hours;
return acc;
}, {});
console.log(Object.values(result));
扩展支持 COUNT 和 AVG(类似 GROUP BY + COUNT + AVG)
只需在中间对象中额外维护计数器和总和,平均值可在最终阶段计算,避免浮点误差累积。
- 每个分组项记录
count、sumAmount、sumHours - reduce 完成后,再映射生成含
avgAmount的结构 - 注意处理除零(count 为 0 时 avg 设为 0 或 NaN)
示例片段:
const grouped = Object.values(frames.reduce((acc, item) => {const key = item.ProjectType;
if (!acc[key]) {
acc[key] = { ProjectType: key, count: 0, sumAmount: 0, sumHours: 0 };
}
acc[key].count++;
acc[key].sumAmount += item.Amount;
acc[key].sumHours += item.Hours;
return acc;
}, {}));
const withAvg = grouped.map(g => ({
...g,
avgAmount: g.count ? (g.sumAmount / g.count).toFixed(2) : 0,
avgHours: g.count ? (g.sumHours / g.count).toFixed(2) : 0
}));
一行式封装成可复用函数
把分组键名和聚合字段抽离,就能快速适配不同数据结构。
- 接受参数:
arr(源数组)、groupKey(字符串,如'ProjectType')、sumFields(字符串数组,如['Amount', 'Hours']) - 内部仍用 reduce,但动态访问属性,避免硬编码
- 返回标准数组,每项含分组键、各字段 sum 值及 count
例如调用:groupBySum(frames, 'ProjectType', ['Amount', 'Hours'])
注意事项与边界处理
实际使用中容易忽略几个关键点:
- 原始数据含
null或undefined数值时,需先转为 0(item.Amount ?? 0),否则累加会得NaN - 分组键本身为
undefined或非字符串类型时,建议统一转为字符串(String(item[groupKey])),防止对象 key 混乱 - 若需多字段联合分组(如
ProjectType + EmployeeGroup),可拼接键名:`${item.ProjectType}-${item.EmployeeGroup}` - 性能敏感场景下,避免在 reduce 内部重复调用
Object.keys(acc).includes(key),直接用acc[key] === undefined判断更高效

















