修复JS对象数组按日期扁平化时缺失分类默认补0的问题
数据集扁平化转换问题修复方案
问题背景
需要将按日期、分类存储的明细数组,转换为按日期聚合的扁平化数组:每个日期对应一个对象,键包含日期字段、所有分类字段,分类字段值为对应KPI的数值,缺失分类数据时默认填充0。
原始数据结构
const CATEGORY = "category"; const DATE = "date"; const dataset = [ { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 }, { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 }, { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 }, { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 }, { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 }, { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }, { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 }, { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 }, { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 } ];
目标输出结构
const result = [ { "date": "2012", "pizza": 1, "fruit": 3, "pasta": 2 }, { "date": "2013", "pizza": 1, "fruit": 3, "pasta": 1 }, { "date": "2014", "pizza": 2, "fruit": 2, "pasta": 1 } ]
核心问题
原有实现存在3个问题:
reduce逻辑以空对象为初始值,只会写入当前日期下实际存在的分类键,没有为全量分类预置默认值,因此缺失分类的键不会出现在结果中- 直接调用
uniqBy/groupBy未加_前缀,在模块化引入lodash的场景会直接报错 datum[kpi] || 0的兜底逻辑存在风险:会将所有假值(包括合法的数值0)误判为缺失值替换
修复逻辑
- 先提取全量分类列表,处理每个日期分组时,先生成基础对象:所有分类默认值为
0,同时预置date字段 - 遍历当前日期下的实际数据条目,覆盖对应分类的KPI值
- 修正lodash方法调用,用严格空值判断替代
||做兜底,避免误判合法数值0
修正后完整代码
import * as _ from 'lodash'; const CATEGORY = "category"; const DATE = "date"; function flatize(dataset, kpi) { // 获取全量分类列表 const categories = _.uniqBy(dataset, CATEGORY).map((d) => d[CATEGORY]); // 按日期分组 const groupByDate = _.groupBy(dataset, DATE); const dates = Object.keys(groupByDate); const flatizedDataset = dates.map((date) => { // 初始化当前日期的基础对象:所有分类默认填0,预置date字段 const dateItem = { [DATE]: date, ...Object.fromEntries(categories.map(cat => [cat, 0])) }; // 遍历当前日期下的实际数据,覆盖对应分类的kpi值 groupByDate[date].forEach(datum => { dateItem[datum[CATEGORY]] = datum[kpi] ?? 0; }); return dateItem; }); return flatizedDataset; } // 测试用例:2012年缺失fruit分类 const dataset = [ { date: "2012", category: "pizza", valueA: 1, valueB: 1, valueC: 3 }, // { date: "2012", category: "fruit", valueA: 3, valueB: 3, valueC: 1 }, { date: "2012", category: "pasta", valueA: 2, valueB: 2, valueC: 2 }, { date: "2013", category: "pizza", valueA: 1, valueB: 2, valueC: 2 }, { date: "2013", category: "fruit", valueA: 3, valueB: 2, valueC: 3 }, { date: "2013", category: "pasta", valueA: 1, valueB: 3, valueC: 1 }, { date: "2014", category: "pizza", valueA: 2, valueB: 1, valueC: 2 }, { date: "2014", category: "fruit", valueA: 2, valueB: 2, valueC: 0 }, { date: "2014", category: "pasta", valueA: 1, valueB: 3, valueC: 1 } ]; const flatDataset = flatize(dataset, "valueA"); console.log(flatDataset); /* 输出结果符合预期,缺失分类自动填充0: [ { date: '2012', pizza: 1, fruit: 0, pasta: 2 }, { date: '2013', pizza: 1, fruit: 3, pasta: 1 }, { date: '2014', pizza: 2, fruit: 2, pasta: 1 } ] */
低版本环境兼容
如果运行环境不支持Object.fromEntries和??空值合并运算符,可以替换为lodash方法实现:
const flatizedDataset = dates.map((date) => { // 替换基础对象生成逻辑 const dateItem = _.reduce(categories, (acc, cat) => { acc[cat] = 0; return acc; }, { [DATE]: date }); // 替换赋值兜底逻辑 groupByDate[date].forEach(datum => { dateItem[datum[CATEGORY]] = _.isNil(datum[kpi]) ? 0 : datum[kpi]; }); return dateItem; });
内容的提问来源于stack exchange,提问作者marielle
相关产品推荐
相关产品推荐

