js-02-merge-intervals
1.000
Challenge · difficulty 2/5
# Merge overlapping intervals
Implement an ES module **`solution.js`** exporting a single function:
```js
export function mergeIntervals(intervals) { /* ... */ }
```
`intervals` is an array of `[start, end]` pairs of numbers, each a **closed** interval with
`start <= end`. Merge all overlapping intervals and return a new array of merged intervals
**sorted ascending by start**.
Rules:
- Intervals that **touch** are merged: `[1, 3]` and `[3, 5]` become `[1, 5]`.
- A merged interval's end is the **maximum** end of the intervals that went into it (handle full
containment, e.g. `[1, 10]` swallows `[2, 3]`).
- The input may be in any order and may contain duplicates. Do **not** mutate the input array.
- An empty input returns `[]`.
Examples:
```js
mergeIntervals([[1, 4], [2, 5]]) // => [[1, 5]]
mergeIntervals([[3, 5], [1, 2]]) // => [[1, 2], [3, 5]]
mergeIntervals([[1, 3], [3, 5]]) // => [[1, 5]]
mergeIntervals([[1, 10], [2, 3], [4, 8]]) // => [[1, 10]]
mergeIntervals([]) // => []
```
tests/solution.test.js
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { mergeIntervals } from "./solution.js";
test("empty input returns empty array", () => {
assert.deepEqual(mergeIntervals([]), []);
});
test("single interval is returned unchanged", () => {
assert.deepEqual(mergeIntervals([[1, 3]]), [[1, 3]]);
});
test("unsorted disjoint input is sorted", () => {
assert.deepEqual(mergeIntervals([[3, 5], [1, 2]]), [[1, 2], [3, 5]]);
});
test("overlapping intervals merge", () => {
assert.deepEqual(mergeIntervals([[1, 4], [2, 5]]), [[1, 5]]);
});
test("touching endpoints merge", () => {
assert.deepEqual(mergeIntervals([[1, 3], [3, 5]]), [[1, 5]]);
});
test("containment keeps the wider interval", () => {
assert.deepEqual(mergeIntervals([[1, 10], [2, 3], [4, 8]]), [[1, 10]]);
});
test("disjoint intervals are left separate", () => {
assert.deepEqual(mergeIntervals([[1, 2], [4, 5]]), [[1, 2], [4, 5]]);
});
test("duplicates collapse to one", () => {
assert.deepEqual(mergeIntervals([[1, 2], [1, 2]]), [[1, 2]]);
});
test("mixed unsorted overlapping and disjoint", () => {
assert.deepEqual(
mergeIntervals([[8, 10], [1, 3], [2, 6], [15, 18]]),
[[1, 6], [8, 10], [15, 18]],
);
});
test("the input array is not mutated", () => {
const input = [[3, 5], [1, 2]];
mergeIntervals(input);
assert.deepEqual(input, [[3, 5], [1, 2]]);
});
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.
Proposed solution
```js
export function mergeIntervals(intervals) {
// Return empty array for empty input
if (intervals.length === 0) {
return [];
}
// Create a shallow copy of the input array to avoid mutation
const sorted = [...intervals];
// Sort intervals by their start value
sorted.sort((a, b) => a[0] - b[0]);
const result = [sorted[0]];
for (let i = 1; i < sorted.length; i++) {
const current = sorted[i];
const last = result[result.length - 1];
// If the current interval overlaps with or touches the last merged interval
if (current[0] <= last[1]) {
// Merge by taking the maximum end value
last[1] = Math.max(last[1], current[1]);
} else {
// No overlap, add the current interval as a new merged interval
result.push(current);
}
}
return result;
}
```Test output (stdout)
TAP version 13 # Subtest: empty input returns empty array ok 1 - empty input returns empty array --- duration_ms: 0.613836 type: 'test' ... # Subtest: single interval is returned unchanged ok 2 - single interval is returned unchanged --- duration_ms: 0.105211 type: 'test' ... # Subtest: unsorted disjoint input is sorted ok 3 - unsorted disjoint input is sorted --- duration_ms: 0.06348 type: 'test' ... # Subtest: overlapping intervals merge ok 4 - overlapping intervals merge --- duration_ms: 0.455464 type: 'test' ... # Subtest: touching endpoints merge ok 5 - touching endpoints merge --- duration_ms: 0.068741 type: 'test' ... # Subtest: containment keeps the wider interval ok 6 - containment keeps the wider interval --- duration_ms: 0.05256 type: 'test' ... # Subtest: disjoint intervals are left separate ok 7 - disjoint intervals are left separate --- duration_ms: 0.04695 type: 'test' ... # Subtest: duplicates collapse to one ok 8 - duplicates collapse to one --- duration_ms: 0.04153 type: 'test' ... # Subtest: mixed unsorted overlapping and disjoint ok 9 - mixed unsorted overlapping and disjoint --- duration_ms: 0.0615 type: 'test' ... # Subtest: the input array is not mutated ok 10 - the input array is not mutated --- duration_ms: 0.112021 type: 'test' ... 1..10 # tests 10 # suites 0 # pass 10 # fail 0 # cancelled 0 # skipped 0 # todo 0 # duration_ms 31.112083