本文介绍如何将原始按年、按月(以数字表示)分散存储的数据,转换为每个年份对应一个包含12个元素的数组(对应12个月),缺失月份自动填充0,并统一替换为英文月份名称。
本文介绍如何将原始按年、按月(以数字表示)分散存储的数据,转换为每个年份对应一个包含12个元素的数组(对应12个月),缺失月份自动填充0,并统一替换为英文月份名称。
在数据可视化或报表生成场景中,常需将稀疏的时序数据(如某年某月的指标值)规整为结构化的时间序列数组——即每个年份对应一个长度为12的 data 数组,索引 0~11 分别代表 1~12 月,缺失月份用 0 填充,同时 categories 固定为标准英文月份列表。
核心思路是:按年分组 → 初始化12个月全零数组 → 按 categories(月份数字,注意从1开始)定位赋值 → 排序输出。推荐使用 Array.prototype.reduce() 高效完成分组与累积构建。
以下是完整可运行的实现方案:
const input = [
{ "name": "2023", "data": 10, "categories": 7 },
{ "name": "2023", "data": 1688, "categories": 8 },
{ "name": "2023", "data": 373, "categories": 9 },
{ "name": "2022", "data": 211, "categories": 9 }
];
const months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
const result = input.reduce((acc, item) => {
// 查找是否已存在该年份的对象
const yearObj = acc.find(obj => obj.name === item.name);
if (!yearObj) {
// 初始化:12个零 + 固定月份名称
const newData = new Array(12).fill(0);
acc.push({
name: item.name,
data: newData,
categories: [...months] // 浅拷贝,避免引用污染
});
}
// 定位并赋值:categories 是 1~12,需转为 0~11 索引
const monthIndex = item.categories - 1;
if (monthIndex >= 0 && monthIndex obj.name === item.name);
target.data[monthIndex] = item.data;
}
return acc;
}, []).sort((a, b) => parseInt(a.name) - parseInt(b.name)); // 按年份升序排列
console.log(result);
// 输出:
// [
// {
// name: "2022",
// data: [0,0,0,0,0,0,0,0,211,0,0,0],
// categories: ["January", ..., "December"]
// },
// {
// name: "2023",
// data: [0,0,0,0,0,0,10,1688,373,0,0,0],
// categories: ["January", ..., "December"]
// }
// ]
✅ 关键要点说明:
- categories 字段是月份编号(1=Jan, 12=Dec),赋值前务必减1转为数组索引;
- 使用 new Array(12).fill(0) 快速初始化全零数组,避免循环;
- categories 数组建议每次新建(如用 [...months]),防止多个年份共享同一引用导致意外修改;
- 最终调用 .sort() 确保年份按数字顺序排列(而非字符串默认字典序),提升可读性与下游兼容性;
- 可选增强:添加输入校验(如 categories 是否为有效整数、name 是否为合法年份格式)以提升鲁棒性。
该方案时间复杂度为 O(n),简洁高效,适用于中等规模数据(数千条以内),且易于扩展(例如支持季度聚合、跨年合并等)。










