如何完善JavaScript代码实现按日期范围拆分全年连续日期数组?
Hey there! Let's break down your problem and fix up that code, plus share a more efficient approach that'll save you some headaches.
First, let's clarify your core goal: You have a full year of consecutive dates (all), plus an array (datesArray) that holds two separate date ranges. You want to extract two separate arrays of dates that fall outside these ranges—either the gaps around/between the two ranges, or dates excluded from each individual range, right?
Let's Start by Fixing Your Existing Code
Your current code has a few small bugs and performance bottlenecks, so let's tweak it first to work as intended:
- Stop repeating expensive operations: You're calling
datesArray.map(Number)every loop, which creates a new array every time. We'll pre-convert this once. - Fix array initialization: You were assigning a single date to
array[j]instead of creating a new array for the outside range. - Clean up the logic: We'll track when we enter/exit a date range to build our outside ranges correctly.
Here's the revised version:
let prevIndex = -2; // Start with a dummy value that won't match real indices const resultArrays = []; let currentOutsideRange = []; // Pre-convert datesArray to numbers once, not every loop const numericDates = datesArray.map(Number); $.each(all, function(i, dateStr) { const currentDate = +dateStr; const index = numericDates.indexOf(currentDate); const isInRange = index !== -1; if (!isInRange) { // Date is outside both ranges—add to current collection currentOutsideRange.push(dateStr); } else { // We hit a date in a range: if we had an ongoing outside range, save it if (currentOutsideRange.length > 0) { resultArrays.push(currentOutsideRange); currentOutsideRange = []; } } // After the last date, save any remaining outside range if (i === all.length - 1 && currentOutsideRange.length > 0) { resultArrays.push(currentOutsideRange); } prevIndex = index; }); // Your two target arrays (will be empty if there's no gap in that spot) const outsideFirstGap = resultArrays[0] || []; const outsideSecondGap = resultArrays[1] || [];
A Much Better (Faster) Approach
Instead of looping through every single date, we can leverage the fact that all is consecutive and datesArray holds two continuous ranges. Here's how to split things cleanly and efficiently:
- Extract the start/end of each range from
datesArray(since it's ordered, we can find the "jump" between the two ranges). - Use Sets for instant lookups (way faster than
indexOffor large arrays—O(1) vs O(n)). - Generate your outside ranges directly (no need to loop every date if you know the bounds).
// Step 1: Convert dates to numbers and sort (just to be safe) const numericDates = datesArray.map(Number).sort((a, b) => a - b); // Step 2: Find the split between the two ranges (where the date sequence breaks) const rangeSplitIndex = numericDates.findIndex((date, idx) => { return idx < numericDates.length - 1 && date + 1 !== numericDates[idx + 1]; }) + 1; // Step 3: Split into two separate range sets for fast lookups const firstRange = new Set(numericDates.slice(0, rangeSplitIndex)); const secondRange = new Set(numericDates.slice(rangeSplitIndex)); // Step 4: Generate your two outside arrays // Array 1: Dates NOT in the first range const outsideFirstRange = all.filter(dateStr => !firstRange.has(+dateStr)); // Array 2: Dates NOT in the second range const outsideSecondRange = all.filter(dateStr => !secondRange.has(+dateStr));
If your goal was to get the two distinct gaps around/between the ranges (e.g., before the first range + between the two ranges, and after the second range), you can adjust this to capture those specific segments using the range bounds:
// Get bounds for each range const firstRangeStart = numericDates[0]; const firstRangeEnd = numericDates[rangeSplitIndex - 1]; const secondRangeStart = numericDates[rangeSplitIndex]; const secondRangeEnd = numericDates[numericDates.length - 1]; const allStart = +all[0]; const allEnd = +all[all.length - 1]; // Generate gap 1: From start of year to end of first range (exclusive) + between first/second ranges const gap1 = all.filter(dateStr => { const date = +dateStr; return date < firstRangeStart || (date > firstRangeEnd && date < secondRangeStart); }); // Generate gap 2: From end of second range (exclusive) to end of year const gap2 = all.filter(dateStr => +dateStr > secondRangeEnd);
Final Notes
- The Set approach is vastly more efficient for large date arrays, especially if you're working with a full year of dates.
- If you already know the start/end dates of your two ranges upfront, you can skip the
findIndexstep and just use those values directly—this will be even faster!
内容的提问来源于stack exchange,提问作者Peter Griffin

