js-sdsl
js-sdsl is a dependency-free TypeScript collection library modeled after C++'s standard data structures. It supplies Stack, Queue, PriorityQueue, Vector, LinkList, Deque, OrderedSet, OrderedMap, HashSet, and HashMap with typed constructors, STL-style mutable iterators, common size and clear methods, and iterable output. Its main value is access to heaps, red-black-tree ordering, indexed double-ended queues, and linked lists that JavaScript's built-ins do not provide.
Worth installing for a measured need for deque, heap, or ordered-tree behavior, especially when translating STL-heavy algorithms. Prefer native collections for ordinary application state, avoid the hash containers for identity-sensitive or frozen keys, and do not follow the currently redirected docs domain.
Use it if
- You need a priority queue, deque, or sorted map or set and do not want to implement and test the underlying heap or red-black tree
- You are porting an algorithm from C++ and STL-shaped methods such as lowerBound, begin, end, pointer, pushBack, and eraseElementByIterator reduce translation mistakes
- You need browser and Node builds, bundled TypeScript declarations, no runtime dependencies, and optional one-container scoped packages
- You have benchmarked a real workload where native Array, Map, or Set is the wrong complexity or memory tradeoff
- You only need key lookup, uniqueness, or a basic stack: native Map, Set, and Array have familiar APIs, engine-level optimization, and no package cost
- You expect standard JavaScript collection semantics: js-sdsl uses methods such as size(), getElementByPos(), setElement(), and eraseElementByKey(), while iterators are mutable objects whose next() advances and returns themselves rather than an IteratorResult
- You need reliable hosted documentation: the README and npm homepage point to js-sdsl.org, which currently redirects to an unrelated survey-smiles.com site, so the generated API links should not be trusted until the project restores control
- You plan to use HashMap or HashSet with frozen or non-extensible object keys: version 4.4.2 tags object keys with a non-enumerable symbol through Object.defineProperty, which can throw and also mutates application objects
- You assume HashMap matches native Map for primitive identity: its internal property table coerces primitive keys, so values such as numeric 1 and string '1' can address the same slot; use native Map when exact SameValueZero key semantics matter
Setup reality
npm install js-sdsl adds no runtime dependencies, peer dependencies, native builds, configuration files, credentials, or environment variables. Version 4.4.2 publishes CommonJS under dist/cjs, ESM under dist/esm, a UMD build, and declarations from dist/esm/index.d.ts. It marks sideEffects false but has no exports map, so resolution and tree shaking depend on your bundler's support for main and module. Import named containers from js-sdsl, or install a scoped package such as @js-sdsl/deque when only one structure is needed. The complete package measured 8.103 KB gzipped, making scoped packages worth considering in browser code. The API intentionally follows STL more than native JavaScript: size is a method, length is a getter, vectors use pushBack and getElementByPos, maps use setElement and getElementByKey, and find returns a mutable container iterator that must be compared with end(). Calling pointer on an end iterator throws. Tree containers use a default < and > comparator; object keys need a stable total-order comparator, and mutating a field used by that comparator corrupts ordering unless the key is removed and reinserted. PriorityQueue is a max-heap by default; a comparator like (a, b) => a - b makes a min-heap, the opposite of what many developers first guess. Vector and PriorityQueue constructors shallow-copy input arrays unless copy is false. Hash containers do not behave exactly like native Map and Set: object keys are tagged with a symbol and frozen objects can fail, while primitive keys pass through an object property table. The official docs domain currently redirects elsewhere, so use shipped declarations, repository source, and tests instead of following the site links. The last npm release was July 2023 even though the repository was pushed in 2026, so pin 4.4.2 and test your chosen containers rather than assuming unreleased source fixes are installed.
Patterns
Process highest-priority work firstuse-max-priority-queue
import { PriorityQueue } from 'js-sdsl'
type Job = { id: string; priority: number }
const jobs = new PriorityQueue<Job>([], (a, b) => b.priority - a.priority)
jobs.push({ id: 'email', priority: 2 })
jobs.push({ id: 'incident', priority: 10 })
console.log(jobs.pop()?.id) // incidentThe built-in default is a max-heap for primitive values. For objects, supply a comparator and test which item reaches top() before relying on it.
Create a min-heap for shortest distanceuse-min-priority-queue
import { PriorityQueue } from 'js-sdsl'
const distances = new PriorityQueue<number>([], (a, b) => a - b)
distances.push(30)
distances.push(5)
distances.push(12)
console.log(distances.top()) // 5
console.log(distances.pop()) // 5Comparator a - b makes smaller values higher priority in this API. pop() removes and returns the top item, or undefined when empty.
Push and pop at both endsuse-double-ended-queue
import { Deque } from 'js-sdsl'
const deque = new Deque<number>()
deque.pushBack(2)
deque.pushFront(1)
deque.pushBack(3)
console.log(deque.front(), deque.back()) // 1 3
deque.popFront()
deque.popBack()Deque is the compelling replacement for repeated Array.shift or unshift on large queues. Empty pops return undefined.
Keep map entries sorted by keyuse-ordered-map
import { OrderedMap } from 'js-sdsl'
const scores = new OrderedMap<string, number>([], (a, b) => a.localeCompare(b))
scores.setElement('zoe', 8)
scores.setElement('ada', 10)
console.log(scores.getElementByKey('ada')) // 10
console.log([...scores]) // [['ada', 10], ['zoe', 8]]The comparator defines key identity as well as order. It must return zero exactly when keys should be treated as the same entry.
Find the first sorted key at or above a targetfind-lower-bound
import { OrderedSet } from 'js-sdsl'
const values = new OrderedSet([10, 20, 40, 50])
const iterator = values.lowerBound(25)
if (!iterator.equals(values.end())) {
console.log(iterator.pointer) // 40
}Never read pointer from end(); it throws. lowerBound returns the first element not less than the target according to the set comparator.
Build a sorted unique sequencededuplicate-in-order
import { OrderedSet } from 'js-sdsl'
const unique = new OrderedSet([5, 1, 5, 3, 1])
console.log(Array.from(unique)) // [1, 3, 5]
unique.insert(2)
unique.eraseElementByKey(5)Use native Set if insertion order is enough. OrderedSet pays tree costs to maintain comparator order and provide bounds operations.
Use indexed STL-style sequence operationsuse-vector
import { Vector } from 'js-sdsl'
const vector = new Vector([10, 20, 30])
vector.pushBack(40)
vector.setElementByPos(1, 25)
vector.insert(2, 99)
console.log(vector.getElementByPos(2)) // 99
console.log([...vector]) // [10, 25, 99, 30, 40]Positions outside the valid range throw RangeError. Native Array is usually simpler unless STL-compatible container methods help the algorithm.
Merge two sorted linked listsuse-linked-list
import { LinkList } from 'js-sdsl'
const odd = new LinkList([1, 3, 5])
const even = new LinkList([2, 4, 6])
odd.merge(even)
console.log([...odd]) // [1, 2, 3, 4, 5, 6]Both lists must already be sorted with compatible ordering. Do not use LinkList for indexed access, which requires traversal.
Choose LIFO or FIFO behavior explicitlyuse-stack-and-queue
import { Queue, Stack } from 'js-sdsl'
const pending = new Queue(['first', 'second'])
console.log(pending.front()) // first
console.log(pending.pop()) // first
const history = new Stack(['first', 'second'])
console.log(history.top()) // second
console.log(history.pop()) // secondQueue and Stack expose only their relevant ends. For small workloads, Array with push/pop or an index pointer is often enough.
Erase through a mutable container iteratorerase-while-iterating
import { OrderedSet } from 'js-sdsl'
const set = new OrderedSet([1, 2, 3, 4, 5])
for (let iterator = set.begin(); !iterator.equals(set.end());) {
if (iterator.pointer % 2 === 0) {
iterator = set.eraseElementByIterator(iterator)
} else {
iterator.next()
}
}
console.log([...set]) // [1, 3, 5]eraseElementByIterator returns the next iterator. iterator.next() mutates and returns the same iterator object, unlike JavaScript's standard iterator protocol.
Keep object identity keys in native Mapavoid-hash-object-keys
const metadata = new Map<object, string>()
const frozenKey = Object.freeze({ id: 1 })
metadata.set(frozenKey, 'ready')
console.log(metadata.get(frozenKey)) // readyjs-sdsl HashMap tags object keys with Object.defineProperty and can fail on frozen keys. Native Map already provides identity-safe object keys without mutation.
Install only the deque package for browser codeinstall-isolated-container
npm install @js-sdsl/deque
import Deque from '@js-sdsl/deque'
const events = new Deque<string>()
events.pushBack('connected')The project publishes each container separately. Confirm the isolated package's import shape and keep its version aligned when mixing it with the main package.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| mnemonist | npm | Use for a broad, JavaScript-oriented collection of specialized structures including heaps, deques, tries, queues, and indexes |
| denque | npm | Use when the only missing structure is a focused double-ended queue with a smaller API and dependency surface |
| heap-js | npm | Use when you need only a heap or priority queue and prefer comparator semantics documented around that single structure |
| immutable | npm | Use when persistent immutable collections and structural sharing matter more than STL-style mutable containers |