IntervalMap.js is a generic TypeScript map for closed numeric intervals. An AVL tree keeps updates and exact lookups balanced, while cached subtree endpoints accelerate point and overlap queries.
Intervals use closed semantics: [a, b] contains both endpoints, and intervals overlap when they share an endpoint.
- Generic
IntervalMap<Value>API with generated declarations - Exact
set,get,has, anddeleteoperations - Point and overlap queries with subtree pruning
- Sorted iteration by interval start and end
- Balanced bulk construction with optional value-aware coalescing
- Defensive interval snapshots that protect tree invariants
- CommonJS, ESM, browser-global, AMD, and browser-ESM builds
npm install intervalmap@rawify/interval is installed as a dependency. Node.js 20 or newer is required.
import Interval from '@rawify/interval';
import IntervalMap from 'intervalmap';
const schedule = new IntervalMap<string>();
schedule.set(new Interval(0, 10), 'setup');
schedule.set(new Interval(8, 20), 'run');
console.log(schedule.get(new Interval(0, 10))); // 'setup'
console.log(schedule.getAt(9)); // 'setup'
console.log(schedule.getAt(9, true).map(entry => entry.value));
// ['setup', 'run']Default and named class exports are available:
import IntervalMap, { IntervalMap as NamedIntervalMap } from 'intervalmap';const Interval = require('@rawify/interval');
const IntervalMap = require('intervalmap');
const map = new IntervalMap();
map.set(new Interval(1, 5), 'active');<script src="https://cdn.jsdelivr.net/npm/@rawify/interval@0.1.0/dist/interval.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/intervalmap@0.1.1/dist/intervalmap.min.js"></script>
<script>
const map = new IntervalMap();
map.set(new Interval(0, 10), 'active');
console.log(map.getAt(5));
</script>The same files are available from unpkg:
<script src="https://unpkg.com/@rawify/interval@0.1.0/dist/interval.min.js"></script>
<script src="https://unpkg.com/intervalmap@0.1.1/dist/intervalmap.min.js"></script>The browser-global build registers an AMD module when define.amd is present.
<script type="module">
import Interval from 'https://cdn.jsdelivr.net/npm/@rawify/interval@0.1.0/dist/interval.min.mjs';
import IntervalMap from 'https://cdn.jsdelivr.net/npm/intervalmap@0.1.1/dist/intervalmap.min.mjs';
const map = new IntervalMap();
map.set(new Interval(0, 10), 'active');
</script>Bundlers can select the minified browser ESM build explicitly:
import IntervalMap from 'intervalmap/browser';const key = new Interval(10, 20);
map.set(key, 'value');
map.get(new Interval(10, 20)); // 'value'
map.has(new Interval(10, 20)); // true
map.delete(new Interval(10, 20)); // truehas() distinguishes a missing key from a key whose value is null or undefined.
map.set(new Interval(0, 10), 'left');
map.set(new Interval(15, 20), 'right');
map.hasOverlap(new Interval(10, 15)); // true
map.getOverlapping(new Interval(8, 16));
// Both entries, sorted by interval start and endTouching endpoints count as overlap because intervals are closed.
map.getAt(9); // First matching value in interval order, or null
map.getAt(9, true); // Every matching entry in interval orderThe single-result overload avoids allocating an array. Use the second overload when all overlapping intervals are required.
The map, entries(), keys(), and values() all iterate in interval order:
for (const { interval, value } of map) {
console.log(interval.a, interval.b, value);
}forEach(callback) uses the same order. toArray() returns a sorted snapshot.
Intervals are cloned on insertion and when returned by public methods. Mutating an input or returned interval does not alter the map. Values remain shared by reference.
IntervalMap.fromArray(entries, mergeSame) sorts entries and builds a minimal-height tree directly:
const map = IntervalMap.fromArray([
{ interval: new Interval(0, 2), value: 'X' },
{ interval: new Interval(2, 5), value: 'X' },
{ interval: new Interval(4, 6), value: 'Y' },
], true);
// X is stored once as [0, 5]; Y remains [4, 6].With mergeSame = true, touching or overlapping intervals with strictly equal values are coalesced. Exact duplicate keys use normal map semantics: the last input value wins. Inputs are not mutated.
| Member | Description |
|---|---|
size |
Number of exact interval keys |
set(interval, value) |
Insert or replace an entry and return the map |
get(interval) |
Return an exact value or null |
has(interval) |
Check for an exact key |
delete(interval) |
Delete an exact key |
hasOverlap(interval) |
Check whether any interval overlaps the query |
getOverlapping(interval) |
Return every overlapping entry |
getAt(point) |
Return the first value containing the point |
getAt(point, true) |
Return every entry containing the point |
entries(), keys(), values() |
Return sorted iterators |
forEach(callback) |
Visit entries in sorted order |
toArray() |
Return a sorted entry snapshot |
clear() |
Remove all entries and return the map |
isEmpty() |
Check whether the map is empty |
getSize() |
Compatibility alias for size |
clone() |
Clone nodes and intervals while retaining value references |
toString() |
Format entries as [a, b] -> value |
IntervalMap.fromArray(entries, mergeSame) |
Build a balanced map from entries |
ESM consumers can import the entry type directly:
import type { IntervalMapEntry } from 'intervalmap';
const entries: IntervalMapEntry<string>[] = [];For TypeScript using CommonJS syntax, it is available through the class namespace:
import IntervalMap = require('intervalmap');
const entries: IntervalMap.Entry<string>[] = [];| Operation | Cost |
|---|---|
set, get, has, delete |
O(log n) |
hasOverlap, single getAt |
Typically O(log n), worst case O(n) |
| All-match queries | Typically O(log n + k), worst case O(n) |
| Sorted iteration | O(n) |
fromArray |
O(n log n) sorting and O(n) construction |
Here, k is the number of returned matches. Heavy overlap can reduce pruning effectiveness.
npm install
npm testThe test command rebuilds every distribution, type-checks ESM and CommonJS consumers, and runs runtime and package-export tests.
Copyright (c) 2026 Robert Eisele
Licensed under the MIT license.