Express.js如何按公共字段合并多条DB查询记录为结构化数组
Express.js 预售数据维度合并实现方案
问题背景
开发中处理数据库返回的preorder_data预售销售数据集时,同零售点、同路线维度下的记录region/area/house/territory/point/dpid/route/retailer_code/date公共字段值完全一致,仅skid(产品编码)、volume(销量)、value(销售额)字段存在差异。需要将同维度多条记录合并为单条结构:公共字段保留为对象顶层属性,差异化的产品销售信息统一归集到sale_info数组,最终输出符合结构的结果数组。
原始数据集示例
const preorder_data = [ { region: "Rajshahi", area: "Bogra", house: "Mr Brothers", territory: "Bogra 1", point: "Bogura", skid: 20000007, dpid: 221, route: "18A", retailer_code: "RAJ-38-00526", volume: 4, value: 5840, date: "2022-01-30T18:00:00.000Z", }, { region: "Rajshahi", area: "Bogra", house: "Mr Brothers", territory: "Bogra 1", point: "Bogura", skid: 20000003, dpid: 221, route: "18A", retailer_code: "RAJ-38-00526", volume: 5, value: 8952, date: "2022-01-30T18:00:00.000Z", }, { region: "Rajshahi", area: "Bogra", house: "M/S Sons", territory: "Bogra 2", point: "Bogura", skid: 20000009, dpid: 221, route: "22B", retailer_code: "RAJ-39-85632", volume: 4, value: 5840, date: "2022-01-30T18:00:00.000Z", }, { region: "Rajshahi", area: "Bogra", house: "M/S Sons", territory: "Bogra 2", point: "Bogura", skid: 20000005, dpid: 221, route: "22B", retailer_code: "RAJ-39-85632", volume: 5, value: 3965, date: "2022-01-30T18:00:00.000Z", },{ region: "Rajshahi", area: "Bogra", house: "M/S Sons", territory: "Bogra 2", point: "Bogura", skid: 20000002, dpid: 221, route: "22B", retailer_code: "RAJ-39-85632", volume: 8, value: 13366, date: "2022-01-30T18:00:00.000Z", }, ];
预期输出结构
[ { region: 'Rajshahi', area: 'Bogra', house: 'Mr Brothers', territory: 'Bogra 1', point: 'Bogura', dpid: 221, route: '18A', retailer_code: 'RAJ-38-00526', date: '2022-01-31', sale_info :[ {skid:20000003,volume:5,value:8952}, {skid:20000007,volume:4,value:5840} ] },{ region: "Rajshahi", area: "Bogra", house: "M/S Sons", territory: "Bogra 2", point: "Bogura", dpid: 221, route: "22B", retailer_code: "RAJ-39-85632", date: '2022-01-31', sale_info :[ {skid:20000002,volume:8,value:13366}, {skid:20000005,volume:5,value:3965}, {skid:20000009,volume:4,value:5840} ] }, ]
原有代码问题
之前编写的forEach逻辑存在3个核心错误,导致无法实现合并效果:
- 未做分组判断:遍历每条记录时都直接生成新数组推入结果,完全没有同维度记录的合并逻辑
- 冗余异步声明:forEach回调加了不必要的
async关键字,该场景无异步操作,且forEach本身不支持异步等待 - 结构不符合要求:将公共字段存入数组而非对象顶层,也没有归集
skid/volume/value三个差异化字段
原有错误代码参考:
var final_arr = []; preorder_data.forEach(async (val, key) => { var temp_arr = []; temp_arr.push(val.region); temp_arr.push(val.area); temp_arr.push(val.house); temp_arr.push(val.territory); temp_arr.push(val.point); temp_arr.push(val.route); temp_arr.push(val.retailer_code); final_arr.push(temp_arr); });
正确实现代码
核心思路是通过唯一分组key做维度聚合:先提取所有公共字段生成唯一分组标识,用临时对象存储分组结果,遍历原始数据时初始化不存在的分组、追加销售数据到已存在的分组,最后将分组对象的值转为数组即可。
const groupMap = {}; // 定义所有公共字段列表 const COMMON_FIELDS = ['region', 'area', 'house', 'territory', 'point', 'dpid', 'route', 'retailer_code', 'date']; preorder_data.forEach(item => { // 拼接公共字段值生成分组唯一key,用特殊分隔符避免字段值拼接冲突 const groupKey = COMMON_FIELDS.map(field => item[field]).join('_||_'); // 当前分组不存在时,初始化分组结构 if (!groupMap[groupKey]) { const groupItem = {}; // 拷贝所有公共字段到分组对象顶层 COMMON_FIELDS.forEach(field => { // 如需格式化date字段(比如转成YYYY-MM-DD格式),可在这里单独判断处理 if (field === 'date') { groupItem[field] = new Date(item[field]).toISOString().split('T')[0]; } else { groupItem[field] = item[field]; } }); // 初始化销售信息空数组 groupItem.sale_info = []; groupMap[groupKey] = groupItem; } // 将当前记录的差异化销售数据推入对应分组的sale_info数组 groupMap[groupKey].sale_info.push({ skid: item.skid, volume: item.volume, value: item.value }); }); // 取出分组映射的所有值,得到最终结果数组 const final_arr = Object.values(groupMap);
补充说明
- 分组key拼接时使用
_||_这类特殊分隔符,是为了避免不同字段值拼接后产生相同字符串导致分组错误 - 如果需要对
sale_info内的产品做排序,可在生成final_arr前遍历每个分组的sale_info数组调用sort方法 - 如果公共字段后续有增减,只需要修改
COMMON_FIELDS数组即可,无需改动核心遍历逻辑
内容的提问来源于stack exchange,提问作者Jone D Adam
相关产品推荐
相关产品推荐

