日历日期排序:如何将日程事件分组生成分层占用概览数组
实现方案
核心思路
这个需求本质是求不同重叠深度对应的日程占用合并区间,group值越大代表该时间段内同时重叠的日程数越多,对应展示在更上层的进度条。我们可以用扫描线算法高效实现:
- 先将所有日程的起止时间转为可比较的时间戳
- 把每个日程的开始记为「层数+1」事件,结束记为「层数-1」事件
- 按时间顺序遍历所有事件,统计每个时间段的重叠层数
- 按层数要求合并对应区间,生成最终的分组数组
代码实现(基于moment.js)
// 把时间字符串转成时间戳(匹配日期格式:日-月-年 时:分) const getTimeStamp = (timeStr) => moment(timeStr, 'DD-MM-YYYY HH:mm').valueOf(); // 时间戳转回原格式字符串 const getTimeStr = (timestamp) => moment(timestamp).format('DD-MM-YYYY HH:mm'); function generateOccupationOverview(events) { // 1. 生成所有扫描线事件 const points = []; events.forEach(event => { const start = getTimeStamp(event.start); const end = getTimeStamp(event.end); points.push({ time: start, type: 1 }); // 开始事件,层数+1 points.push({ time: end, type: -1 }); // 结束事件,层数-1 }); // 2. 排序扫描线:时间升序,同时间先处理结束事件,避免误判重叠 points.sort((a, b) => { if (a.time !== b.time) return a.time - b.time; return a.type - b.type; }); // 3. 遍历得到每个时间段的重叠层数 const timeSegments = []; let currentLevel = 0; let prevTime = null; points.forEach(point => { if (prevTime !== null && point.time > prevTime && currentLevel > 0) { timeSegments.push({ start: prevTime, end: point.time, level: currentLevel }); } currentLevel += point.type; prevTime = point.time; }); // 4. 按group层级合并对应区间 const maxLevel = Math.max(...timeSegments.map(s => s.level)); const result = []; for (let group = 1; group <= maxLevel; group++) { // 筛选出层数>=当前group的所有时间段,合并连续区间 const targetSegments = timeSegments.filter(s => s.level >= group); if (targetSegments.length === 0) continue; let currentMerge = { ...targetSegments[0] }; for (let i = 1; i < targetSegments.length; i++) { const seg = targetSegments[i]; if (seg.start === currentMerge.end) { currentMerge.end = seg.end; } else { result.push({ start: getTimeStr(currentMerge.start), end: getTimeStr(currentMerge.end), group }); currentMerge = { ...seg }; } } // 存入最后一个合并区间 result.push({ start: getTimeStr(currentMerge.start), end: getTimeStr(currentMerge.end), group }); } return result; }
传入你提供的示例events数组执行函数,输出结果和你给出的预期ooArray完全一致。
内容的提问来源于stack exchange,提问作者Jeramai
相关产品推荐
相关产品推荐

