本文介绍如何将原始按年、按月(以数字表示)分散存储的数据,转换为每个年份对应一个包含12个元素的数组(代表全年各月),缺失月份自动补零,并统一映射为英文月份名称。
本文介绍如何将原始按年、按月(以数字表示)分散存储的数据,转换为每个年份对应一个包含12个元素的数组(代表全年各月),缺失月份自动补零,并统一映射为英文月份名称。
在数据可视化或报表生成场景中,常需将稀疏的月度指标(如 categories: 7 表示七月)归并为结构规整的年度时间序列——即每个年份对应一个长度为 12 的数值数组,索引 0 对应一月,11 对应十二月,未出现的月份默认填充 0。以下是一个高效、可读性强的实现方案。
核心思路
使用 Array.prototype.reduce() 逐项聚合原始数据:
- 按 name(年份)分组;
- 为每个新出现的年份初始化一个含 12 个 0 的 data 数组和标准 categories;
- 利用 categories 字段(1–12)作为索引,将 data 值写入对应位置(注意:需减 1 转为 0-based 索引);
- 最后按年份升序排序,确保输出稳定。
完整实现代码
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 yearEntry = acc.find(({ name }) => name === item.name);
if (!yearEntry) {
const newData = new Array(12).fill(0);
acc.push({
name: item.name,
data: newData,
categories: [...months] // 避免引用共享数组
});
}
// 更新对应年份的 data 数组(注意:categories 是 1-based)
const idx = item.categories - 1;
if (idx >= 0 && idx name === item.name);
target.data[idx] = item.data;
}
return acc;
}, []).sort(({ name: a }, { name: b }) => parseInt(a) - parseInt(b));
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"]
// }
// ]
注意事项
- ✅ 索引安全:代码显式校验 item.categories - 1 是否在 [0, 11] 范围内,防止越界写入;
- ✅ 深拷贝 categories:使用 [...months] 确保各年份的 categories 数组相互独立;
- ✅ 年份排序健壮性:使用 parseInt() 而非 +a - b,避免字符串隐式转换异常(如 "2023" → 2023);
- ⚠️ 若原始数据中存在非法月份(如 categories: 13),该条目将被忽略——可根据业务需要改为报错或警告;
- ? 如需支持多维数据(如每个 data 是对象),只需扩展 data 初始化逻辑与赋值方式。
此方案简洁、无依赖、性能良好(O(n) 时间复杂度),适用于前端图表库(如 Chart.js、ECharts)所需的标准时间序列输入格式。










