
本文介绍如何将原始的“年份+单月数值”扁平数据,合并、分组并填充为每个年份对应12个月的标准化数组,缺失月份自动补0,并将数字月份映射为英文月份名称。
本文介绍如何将原始的“年份+单月数值”扁平数据,合并、分组并填充为每个年份对应12个月的标准化数组,缺失月份自动补0,并将数字月份映射为英文月份名称。
在数据可视化或报表生成场景中,常需将稀疏的月度指标(如 { name: "2023", data: 1688, categories: 8 })转换为结构统一的年度序列——即每个年份对应一个长度为12的 data 数组和固定顺序的 categories(12个月英文名),未出现的月份值设为 0。这本质上是按年分组 + 月序索引填充 + 缺失补零的组合操作。
以下是推荐的实现方案,使用 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(({ name }) => name === item.name);
if (!yearObj) {
// 初始化新年度:data 全为 0,categories 固定
const newData = new Array(12).fill(0);
acc.push({
name: item.name,
data: newData,
categories: [...months] // 浅拷贝避免引用污染
});
}
// 定位当前项所属月份索引(注意:categories 是 1-based,需减 1)
const monthIndex = item.categories - 1;
if (monthIndex >= 0 && monthIndex 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"]
// }
// ]
✅ 关键设计说明:
- 使用 reduce 累积器统一管理分组逻辑,避免多次遍历;
- new Array(12).fill(0) 确保每月初始值为 0,天然支持“缺失即零”语义;
- item.categories - 1 将输入中的月份编号(如 7 → July)转为数组索引(6);
- 显式校验 monthIndex 范围(0–11),提升鲁棒性;
- 最终 sort() 按数值升序排列年份,保证输出可预测(如 2022 在前、2023 在后)。
⚠️ 注意事项:
- 若原始数据中 categories 可能为字符串(如 "7"),建议先 parseInt(item.categories) 再计算索引;
- categories 数组建议定义为常量(如示例中的 months),便于复用与国际化扩展;
- 如需支持跨年动态范围(如含 2021/2024),当前逻辑仍适用,reduce 自动适配新增年份。
该方案简洁、高效、可读性强,适用于前端图表库(如 Chart.js、ECharts)所需的标准化数据预处理流程。










