如何用JavaScript将重叠日期范围拆分为无重叠区块?
解答:重叠日期区间的奖金聚合实现
Hey there! 先给你明确一下:你正在做的这个操作,专业上可以叫做重叠区间聚合(Overlapping Interval Aggregation),或者更具体点,是将连续/重叠的日期区间**离散化(Discretization)**为无重叠的子区间,并计算每个子区间的叠加值(这里就是奖金总和)。
需求回顾
你拥有一组包含日期范围与奖金的数组,目标是生成无重叠的日期范围数组,每个区间对应所有覆盖它的原区间的奖金总和,同时还要处理日期之间可能存在的间隔。
实现思路拆解
你的代码思路非常清晰,核心逻辑可以拆成这几步:
- 提取所有原区间的起始和结束日期,排序后得到所有关键时间节点
- 用相邻的时间节点生成无重叠的子区间(包括日期间隔的部分)
- 遍历每个子区间,检查哪些原区间与它相交,累加对应的奖金值
格式化后的完整代码
const _ = require('lodash'); const Moment = require('moment'); const MomentRange = require('moment-range'); const moment = MomentRange.extendMoment(Moment); let bonuses = [ {start: new Date('Jan 15, 2018 23:00:00 GMT+0200'), end: new Date('Jan 18, 2018 23:59:59 GMT+0200'), bonus: 30, preSale: true}, {start: new Date('Jan 17, 2018 00:00:00 GMT+0200'), end: new Date('Jan 29, 2018 13:00:00 GMT+0200'), bonus: 25, preSale: true}, {start: new Date('Feb 12, 2018 00:00:00 GMT+0200'), end: new Date('Feb 18, 2018 23:59:59 GMT+0200'), bonus: 20, preSale: false}, {start: new Date('Feb 19, 2018 00:00:00 GMT+0200'), end: new Date('Feb 27, 2018 23:59:59 GMT+0200'), bonus: 15, preSale: false}, {start: new Date('Feb 26, 2018 00:00:00 GMT+0200'), end: new Date('Mar 4, 2018 23:59:59 GMT+0200'), bonus: 10, preSale: false}, {start: new Date('Mar 5, 2018 00:00:00 GMT+0200'), end: new Date('Mar 11, 2018 23:59:59 GMT+0200'), bonus: 5, preSale: false}, ]; // 为每个奖金区间生成moment-range对象 _.map(bonuses, o => o.range = moment.range(o.start, o.end)); // 收集所有日期点并排序 let dates = []; _.each(bonuses, o => { dates.push(o.start); dates.push(o.end); }); dates.sort((a, b) => a - b); // 生成无重叠的子区间(跳过空区间) let ranges = []; for(let i = 1; i < dates.length; i++) { const start = dates[i-1]; const end = dates[i]; // 避免生成起始/结束时间完全相同的空区间 if (start.getTime() !== end.getTime()) { ranges.push({ start, end, range: moment.range(start, end) }); } } // 计算每个子区间的总奖金 for (let range of ranges) { range.bonus = 0; for (let bonus of bonuses) { if (range.range.intersect(bonus.range)) { range.bonus += bonus.bonus; } } } // 输出结果 _.each(ranges, r => console.log(r.start, ' - ', r.end, ' => ', r.bonus));
小优化说明
我在生成子区间时加了一个判断,跳过起始和结束日期完全相同的情况,避免生成没有实际时长的空区间,让结果更严谨。
内容的提问来源于stack exchange,提问作者keeeenion
相关产品推荐
相关产品推荐

