使用Lodash与Reduce重塑JSON时日期分组数据缺失问题排查
问题分析:按日期分组后数据不全的原因及修复方案
问题现象
你通过csvtojson解析CSV后,用reduce按日期分组时发现2020-06-29只返回了1条数据,而实际应该包含多个时间点的监测数据——整体JSON结构符合预期,但数据缺失严重。
问题根源
看你写的reduce逻辑:
const res = json_from_csv_filtered.reduce((acc, val) => { const find = acc.find(el => el.date === val.datetime.substring(0, 10)); const { datetime, ...obj } = val; if (find) { find.values[0] = { ...find.values[0], ...obj }; } else { acc.push({ date: datetime.substring(0, 10), values: [{ t: datetime, ...obj }] }); } return acc; }, []);
核心问题出在这里:同一日期下,你始终把新的污染物数据合并到values数组的第一个元素里,完全忽略了时间点(t)的差异。
举个实际场景的例子:假设2020-06-29有两个时间点(00:59和01:59)的监测数据:
- 处理00:59的Benzene时,代码会创建日期分组,
values里新增{t: "2020-06-29T00:59:59.000Z", Benzene:4.3} - 处理00:59的NO2和O3时,会合并到这个第一个元素里,得到完整的00:59时间点数据
- 处理01:59的Benzene时,代码直接把这个值合并到
values[0]里,覆盖掉原来的Benzene值,同时01:59这个时间点的记录完全丢失
最终就导致同一日期下只能保留第一个被处理的时间点数据,其他时间点的污染物都会被“合并覆盖”,时间点本身不会新增。
修复方案
需要在同一日期下,再按时间点(t)进行二次分组:找到日期对应的分组后,先在values里查找是否有相同t的对象,有就合并污染物,没有就新增一条values项。修改后的reduce代码如下:
const res = json_from_csv_filtered.reduce((acc, val) => { const dateStr = val.datetime.substring(0, 10); const { datetime, ...obj } = val; // 先查找对应日期的分组 const dateGroup = acc.find(el => el.date === dateStr); if (dateGroup) { // 在日期分组内查找对应时间点的记录 const timePoint = dateGroup.values.find(item => item.t === datetime); if (timePoint) { // 已有该时间点,合并污染物数据 Object.assign(timePoint, obj); } else { // 无该时间点,新增一条记录 dateGroup.values.push({ t: datetime, ...obj }); } } else { // 无该日期分组,新建分组并添加第一个时间点记录 acc.push({ date: dateStr, values: [{ t: datetime, ...obj }] }); } return acc; }, []);
额外优化建议
考虑到你已经引入了Lodash,可以用_.find替代原生Array.find提升查找性能:
// 替换原有的dateGroup查找逻辑 const dateGroup = _.find(acc, { date: dateStr });
如果CSV数据量较大,原生数组find的性能会明显下降,建议用临时对象存储分组数据,最后再转成目标数组结构,性能会好很多:
// 用对象临时存储按日期+时间点分组的数据 const tempMap = json_from_csv_filtered.reduce((map, val) => { const dateStr = val.datetime.substring(0, 10); const { datetime, ...obj } = val; // 按日期建一级键 if (!map[dateStr]) map[dateStr] = {}; // 按时间点建二级键 if (!map[dateStr][datetime]) { map[dateStr][datetime] = { t: datetime }; } // 合并当前污染物数据 Object.assign(map[dateStr][datetime], obj); return map; }, {}); // 把临时对象转成预期的数组结构 const res = Object.entries(tempMap).map(([date, timePoints]) => ({ date, values: Object.values(timePoints) }));
内容的提问来源于stack exchange,提问作者EanX
相关产品推荐
相关产品推荐

