JavaScript简化合并同日期同名称数组对象的最优方法(优先jQuery实现)
按日期+名称维度合并对象数组的高效实现
方案核心思路
你之前设想的「多轮遍历直到数组长度不再变化」的方案,时间复杂度高达O(n²),数据量稍大就会出现性能卡顿。更高效的做法是单次遍历+字典缓存,整体时间复杂度仅为O(n),不需要反复扫全量数组。
实现逻辑非常简单:
- 用Map作为缓存容器,存储已经遇到过的「日期+名称」组合对应的合并后条目(用Map比普通JS对象更可靠,不会有原型链键冲突的问题)
- 遍历原始数组的每一条数据,拼出唯一的分组键:把日期转成时间戳和名称拼接,避免Date对象因为引用不同导致比对失效的问题
- 如果当前分组键已经在缓存中存在,就把当前条目的l、h、o、c四个数值字段累加到缓存对应条目的数值上
- 如果当前分组键在缓存中不存在,就把当前条目做浅拷贝后存入缓存
- 遍历完成后,把缓存中存储的所有条目提取出来,就是最终合并完成的数组
原生JS实现代码
const dataSource = [ { date: new Date(1994, 2, 2), name: "a", l: 24.00, h: 25.00, o: 25.00, c: 24.875 }, { date: new Date(1994, 2, 2), name: "a", l: 23.625, h: 25.125, o: 24.00, c: 24.875 }, { date: new Date(1994, 2, 3), name: "a", l: 26.25, h: 28.25, o: 26.75, c: 27.00 }, { date: new Date(1994, 2, 4), name: "c", l: 26.50, h: 27.875, o: 26.875, c: 27.25 } ]; const mergeGroup = (originList) => { const cache = new Map(); originList.forEach(item => { // 用时间戳拼接生成分组键,保证同日期同名称的条目能被分到同一组 const groupKey = `${item.date.getTime()}_${item.name}`; if (cache.has(groupKey)) { const mergedItem = cache.get(groupKey); mergedItem.l += item.l; mergedItem.h += item.h; mergedItem.o += item.o; mergedItem.c += item.c; } else { // 浅拷贝存入缓存,避免修改原始数组数据 cache.set(groupKey, {...item}); } }); return Array.from(cache.values()); } const mergedResult = mergeGroup(dataSource);
上面的代码运行后得到的结果和你给出的示例结构完全一致。
关于工具函数的说明
jQuery中的$.grep本质是数组筛选方法,只支持根据条件匹配过滤条目,不支持跨条目的聚合累加,用它实现这个场景会非常绕,效率也不高。
如果日常开发中常用Lodash这类工具库,也可以用链式调用写出更简洁的版本,底层逻辑和上面的缓存方案完全一致:
import _ from 'lodash'; const mergedResult = _.chain(dataSource) // 按规则分组 .groupBy(item => `${item.date.getTime()}_${item.name}`) // 逐组合并数值字段 .map(groupItems => ({ date: groupItems[0].date, name: groupItems[0].name, l: _.sumBy(groupItems, 'l'), h: _.sumBy(groupItems, 'h'), o: _.sumBy(groupItems, 'o'), c: _.sumBy(groupItems, 'c') })) .value();
常见踩坑提醒
- 不要直接把Date对象作为分组键,因为对象类型的键会被隐式转成字符串,同一个日期的两个Date实例可能生成不一样的字符串,导致分组失效
- 存缓存的时候尽量做浅拷贝,避免后续修改合并后的数组影响原始数据源
- 不要用「多轮循环直到数组长度不变」的方案,数组规模超过100条时性能就会明显低于单次遍历的方案
内容的提问来源于stack exchange,提问作者Luke Vanzweden
相关产品推荐
相关产品推荐

