
本文详解如何利用 JavaScript 的 reduce 方法,将多个具有相同营业时段(openTime/closeTime)的营业日对象合并为分组结构,实现从扁平数组到逻辑分组对象数组的高效转换。
本文详解如何利用 javascript 的 `reduce` 方法,将多个具有相同营业时段(opentime/closetime)的营业日对象合并为分组结构,实现从扁平数组到逻辑分组对象数组的高效转换。
在日常开发中,我们常需对结构化数据进行“按特征聚类”处理——例如,将一周内多个营业日按统一的营业时间(如 openTime 和 closeTime)归并成一组。原始数据是每个工作日独立的对象,而目标是将 closeTime 与 openTime 完全一致的记录合并为一个对象,其 day 字段变为字符串数组,其余字段(如 validFrom、validTo)保持唯一且一致。
关键在于:分组依据不是单个字段(如 validFrom),而是复合键(openTime + closeTime)。用户最初尝试仅用 validFrom 分组,导致逻辑错误——因为所有项 validFrom 和 validTo 相同,但真正需要区分的是营业时段差异。
以下是推荐的解决方案,使用 reduce 实现一次遍历完成分组:
const result = data.reduce((acc, curr) => {
// 使用 openTime + closeTime 作为复合分组键
const key = `${curr.openTime}-${curr.closeTime}`;
// 查找已存在的分组(可选:用 Map 或 Object 提升性能;此处用 findIndex 保证顺序)
const existingIndex = acc.findIndex(item =>
item.openTime === curr.openTime && item.closeTime === curr.closeTime
);
if (existingIndex === -1) {
// 新建分组:复制当前项,仅将 day 转为数组
acc.push({
day: [curr.day],
openTime: curr.openTime,
closeTime: curr.closeTime,
validFrom: curr.validFrom,
validTo: curr.validTo
});
} else {
// 向已有分组的 day 数组追加当前 day
acc[existingIndex].day.push(curr.day);
}
return acc;
}, []);
✅ 输出结果完全匹配预期:
- 第一组包含 openTime: "0800" & closeTime: "2000" 的 5 个平日(Monday–Friday, Wednesday, Tuesday, Thursday);
- 第二组包含 openTime: "0845" & closeTime: "1630" 的周末两天(Saturday, Sunday);
- 所有 validFrom/validTo 保持原值,无冗余或丢失。
? 注意事项与优化建议:
- 稳定性保障:findIndex 确保分组顺序与输入顺序一致(如周一优先于周日),若需严格按星期排序(Mon→Sun),可在最终结果上对 day 数组调用 .sort() 配合自定义星期映射;
-
性能进阶:当数据量较大(>1000 条)时,可用 Map 替代 findIndex 实现 O(1) 查找:
const map = new Map(); data.forEach(item => { const key = `${item.openTime}-${item.closeTime}`; if (!map.has(key)) { map.set(key, { day: [], ...Omit(item, ['day']) }); } map.get(key).day.push(item.day); }); const result = Array.from(map.values()); - 健壮性增强:生产环境建议增加空值校验(如 curr?.openTime)及字段一致性断言(确保同组内 validFrom/validTo 确实相同,否则抛错提示数据异常)。
掌握这种基于复合键的 reduce 分组模式,不仅能解决营业时间聚合问题,还可轻松迁移至订单归集、权限合并、配置去重等各类前端数据规整场景。











