JavaScript实现:计算数组中指定日期倒推的连续天数
问题:计算从指定日期倒推的连续天数函数
需要编写一个函数,接收可能未排序的日期数组(date_array)作为输入,计算从指定日期(start_date)倒推的连续天数(不包含start_date本身)。
示例:
start_date = 23.01.2023date_array = [
01.01.2023,
02.01.2023,
20.01.2023, <- 连续天数第3天
21.01.2023, <- 连续天数第2天
22.01.2023, <- 连续天数第1天
22.01.2023, <- 连续天数第1天
23.01.2023,
24.01.2023]结果:
streak_length: 3 days
我原本的思路是从start_date倒推创建日期范围,统计数组中处于该范围内的日期数量,每次循环将范围结束日期再往前推一天,直到统计数量不再增长。但现在遇到了start_date在循环前被end_date覆盖的问题,希望得到问题解决方法或更优实现方案。
现有代码如下:
const dateArray = [ new Date("2022-12-31"), new Date("2023-01-02"), // 连续天数第3天 new Date("2023-01-03"), // 连续天数第2天 new Date("2023-01-03"), // 连续天数第2天 new Date("2023-01-04"), // 连续天数第1天 new Date("2023-01-05")]; const d_start = new Date("2023-01-05"); function currentStreak(dateArray, d_start) { // dateArray: 事件发生日期的数组 // d_start: 连续天数的起始日期,不包含在内 // 创建范围 d_start ... d_end,d_end初始为d_start前一天 let d_end = d_start d_end.setDate(d_start.getDate() - 1) let countPrev = -1 let count = 0 let streakCount = 0 // 统计数组中在范围内的元素数量,直到数量不再增长时停止循环 while (count > countPrev) { countPrev = count // 统计数组中在start和end日期之间的元素数量 count = dateArray.reduce((accumulator, currentValue) => { if((d_start > currentValue) && (currentValue > d_end)) { accumulator += 1 } return accumulator }, 0) // 设置下一次迭代的新结束日期 d_end = d_end.setDate(d_end.getDate() - 1) streakCount = streakCount+1 } return count; } currentStreak(dateArray, d_start)
问题分析与解决方案
当前代码的核心问题
- Date对象引用覆盖:
let d_end = d_start是直接赋值引用,不是创建新日期对象,调用d_end.setDate()会直接修改原d_start的值,这就是start_date被覆盖的原因。 - 逻辑方向错误:连续天数只需要判断某一天是否存在记录,不需要统计当天的记录数量,原代码的统计逻辑会导致误判。
- 类型转换错误:
d_end = d_end.setDate(...)会把Date对象转为时间戳数字,后续调用getDate()会直接报错。
修复后的实现代码
const dateArray = [ new Date("2022-12-31"), new Date("2023-01-02"), new Date("2023-01-03"), new Date("2023-01-03"), new Date("2023-01-04"), new Date("2023-01-05")]; const d_start = new Date("2023-01-05"); function currentStreak(dateArray, d_start) { // 标准化日期:去除时分秒干扰,转为时间戳存入Set,实现O(1)查询 const dateSet = new Set(dateArray.map(date => { const normalized = new Date(date); normalized.setHours(0, 0, 0, 0); return normalized.getTime(); })); let streakCount = 0; const checkDate = new Date(d_start); // 从start_date的前一天开始检查 checkDate.setDate(checkDate.getDate() - 1); // 倒推检查每一天是否存在记录 while (true) { const checkTimestamp = checkDate.setHours(0, 0, 0, 0); if (dateSet.has(checkTimestamp)) { streakCount++; checkDate.setDate(checkDate.getDate() - 1); } else { // 遇到无记录的日期,终止循环 break; } } return streakCount; } console.log(currentStreak(dateArray, d_start)); // 输出:3
更优思路说明
- 日期标准化:把所有日期的时分秒重置为0,避免不同时间点的同一日期被误判,同时用Set存储时间戳,查询效率远高于遍历数组。
- 直接倒推检查:从目标日期前一天开始,每天往前推,只要当天存在记录就计数,直到遇到空日期为止,逻辑简单且高效。
内容的提问来源于stack exchange,提问作者molotovsoda
相关产品推荐
相关产品推荐

