JavaScript:如何实现按时间周期(年/季/月/周等)对{数值,日期}对象列表的分组汇总?
通用时间周期数据汇总解决方案
核心思路
这需求我之前做报表系统时经常碰到,核心就是两步:先把每条数据的日期转换成对应周期的唯一标识(比如把2017-10-24转成月度标识2017-10、季度标识2017-Q4),再按这个标识分组求和就行,灵活性拉满。
实现代码
先写个日期转周期标识的工具函数,再结合reduce完成分组求和,整个逻辑封装成通用函数,想切换周期直接传参数就行:
// 工具函数:将日期字符串转换为指定周期的唯一标识 function getPeriodKey(dateStr, periodType) { const date = new Date(dateStr); if (isNaN(date.getTime())) { throw new Error(`无效日期格式:${dateStr}`); } const year = date.getFullYear(); const month = date.getMonth() + 1; // 原生Date月份从0开始,转成1-12的常规格式 switch(periodType) { case 'year': return `${year}`; case 'quarter': const quarter = Math.ceil(month / 3); return `${year}-Q${quarter}`; case 'month': return `${year}-${String(month).padStart(2, '0')}`; case 'week': // 生成ISO标准周标识(比如2017-W43) const isoWeek = date.toLocaleDateString('en-US', { week: '2-digit', year: 'numeric' }).replace('/', '-W'); return isoWeek; default: throw new Error(`暂不支持该周期类型:${periodType}`); } } // 通用时间周期汇总函数 function aggregateByTimePeriod(rawData, periodType) { // 第一步:按周期标识分组累加quantity const groupedData = rawData.reduce((acc, item) => { const periodKey = getPeriodKey(item.date, periodType); acc[periodKey] = (acc[periodKey] || 0) + item.quantity; return acc; }, {}); // 第二步:把分组后的对象转换成目标格式的数组 return Object.entries(groupedData).map(([period, quantity]) => ({ quantity, period })); }
使用示例
用你提供的原始数据测试不同周期的效果:
const rawData = [ { "quantity": 44000, "date": "2017-10-24" }, { "quantity": 44000, "date": "2017-10-24" }, { "quantity": 44000, "date": "2017-10-27" }, { "quantity": 44000, "date": "2017-10-27" }, { "quantity": 44000, "date": "2017-11-16" } ]; // 按月汇总(就是你要的目标结果) const monthlyResult = aggregateByTimePeriod(rawData, 'month'); console.log(monthlyResult); // 输出:[{ quantity: 176000, period: "2017-10" }, { quantity: 44000, period: "2017-11" }] // 按季度汇总 const quarterlyResult = aggregateByTimePeriod(rawData, 'quarter'); console.log(quarterlyResult); // 输出:[{ quantity: 220000, period: "2017-Q4" }] // 按年度汇总 const yearlyResult = aggregateByTimePeriod(rawData, 'year'); console.log(yearlyResult); // 输出:[{ quantity: 220000, period: "2017" }]
扩展说明
- 如果需要新增周期(比如半月、旬),直接在
getPeriodKey的switch里加分支就行,完全不用改核心汇总逻辑 - 要是项目里用了Day.js、Moment.js这类日期库,可以把
getPeriodKey换成更简洁的格式化API,比如Day.js的dayjs(dateStr).format('YYYY-MM') - 加了无效日期的校验,避免脏数据导致程序崩溃,你也可以根据需求改成静默忽略错误数据
内容的提问来源于stack exchange,提问作者freenrg
相关产品推荐
相关产品推荐

