Skip to content

Commit 83faeab

Browse files
committed
refactor: address comments from #62
1 parent 59d6503 commit 83faeab

File tree

2 files changed

+15
-27
lines changed

2 files changed

+15
-27
lines changed

src/util/__tests__/luxon-utils.test.const.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const MERGE_INTERSECTING_TEST_CASES = [
4646
* Converts an ISO 8601 string into a validated Luxon Interval.
4747
* @param iso - The ISO 8601 string representing the interval.
4848
* @returns The valid Luxon Interval derived from the provided string.
49-
* @throws {Error} If the ISO string does not produce a valid interval.
49+
* @throws If the ISO string does not produce a valid interval.
5050
*/
5151
function toInterval(iso: string): Interval<true> {
5252
const interval = Interval.fromISO(iso);

src/util/luxon-utils.ts

+14-26
Original file line numberDiff line numberDiff line change
@@ -54,32 +54,20 @@ export function assertValidDateTimeFormat(
5454
}
5555

5656
/**
57-
* Merges intersecting intervals from an unsorted array.
58-
*
59-
* This function takes an unsorted array of valid Luxon Interval objects and returns a new
60-
* array where any overlapping intervals are merged. If the input array contains fewer than
61-
* two intervals, a shallow copy is returned. Otherwise, the intervals are sorted by their start
62-
* and end times before sequentially merging any that intersect. The union of intersecting intervals
63-
* is validated to ensure correctness.
64-
* @param unsorted - An unsorted array of Interval objects.
65-
* @returns An array of Interval objects with merged overlapping intervals.
57+
* Merge an array of Intervals into an equivalent minimal set of Intervals.
58+
* Only combines **intersecting** intervals. Use {@link Interval.merge} to, additionally, merge adjacent intervals.
59+
* @param input - intervals to merge
60+
* @returns an equivalent minimal set of Intervals without any intersections.
6661
*/
67-
export function mergeIntersecting(unsorted: Interval<true>[]): Interval<true>[] {
68-
if (unsorted.length < 2) {
69-
return [...unsorted];
70-
} else {
71-
const [first, ...sortedRest] = sortBy(unsorted, ["start", "end"]);
72-
const sortedNonIntersecting = [first];
73-
for (const next of sortedRest) {
74-
const last = sortedNonIntersecting[sortedNonIntersecting.length - 1];
75-
if (last.intersection(next)) {
76-
const union = last.union(next);
77-
assertValidLuxonValue(union);
78-
sortedNonIntersecting[sortedNonIntersecting.length - 1] = union;
79-
} else {
80-
sortedNonIntersecting.push(next);
81-
}
62+
export function mergeIntersecting(input: Interval<true>[]): Interval<true>[] {
63+
return sortBy(input, ["start", "end"]).reduce<Interval<true>[]>((output, next) => {
64+
const prevIndex = output.length - 1;
65+
if (prevIndex >= 0 && output[prevIndex].intersection(next)) {
66+
const union = output[prevIndex].union(next);
67+
assertValidLuxonValue(union);
68+
return output.toSpliced(prevIndex, 1, union);
69+
} else {
70+
return [...output, next];
8271
}
83-
return sortedNonIntersecting;
84-
}
72+
}, []);
8573
}

0 commit comments

Comments
 (0)