@rimbu/sorted
Version:
Immutable SortedMap and SortedSet implementations for TypeScript
299 lines (211 loc) • 10.3 kB
Markdown
<p align="center">
<img src="https://github.com/rimbu-org/rimbu/raw/main/assets/rimbu_logo.svg" height="96" alt="Rimbu Logo" />
</p>
<div align="center">
[](https://www.npmjs.com/package/@rimbu/sorted)





</div>
# `@rimbu/sorted`
**Fast, immutable sorted maps and sets for TypeScript & JavaScript.**
`@rimbu/sorted` provides efficient, type-safe **SortedMap** and **SortedSet** implementations.
They keep elements in a **deterministic sorted order** using a configurable comparator (`Comp`),
while offering immutable, persistent semantics and a rich, map/set‑like API.
Use them whenever you need **ordered iteration** (by key or value), **range queries**, or
index‑based slicing on top of regular map/set behaviour.
---
## Table of Contents
1. [Why `@rimbu/sorted`?](#why-rimbu-sorted)
2. [Feature Highlights](#feature-highlights)
3. [Quick Start](#quick-start)
4. [Core Concepts & Types](#core-concepts--types)
5. [Working with `SortedMap`](#working-with-sortedmap)
6. [Working with `SortedSet`](#working-with-sortedset)
7. [Custom Comparators & Contexts](#custom-comparators--contexts)
8. [Installation](#installation)
9. [FAQ](#faq)
10. [Ecosystem & Integration](#ecosystem--integration)
11. [Contributing](#contributing)
12. [License](#license)
---
## Why `@rimbu/sorted`?
Plain `Map` / `Set` in JS don’t know about ordering beyond insertion order.
`@rimbu/sorted` focuses on:
- **Sorted views** – entries (for maps) and values (for sets) are always kept in sorted order.
- **Range queries** – stream or slice by key or value ranges and by index ranges.
- **Index-based access** – get the _n‑th_ smallest key or value directly.
- **Immutable operations** – updates return new instances with structural sharing.
If you ever needed to sort a map’s entries repeatedly, maintain parallel sorted arrays,
or build your own tree structures, `SortedMap` / `SortedSet` are usually a better fit.
---
## Feature Highlights
- **SortedMap & SortedSet** – ordered by a configurable comparator (`Comp`‑like interface).
- **Efficient lookups** – map/set semantics with \\(O(\log n)\\)-style updates on persistent trees.
- **Index-aware APIs** – `getAtIndex`, `sliceIndex`, `take`, `drop`, `streamRange`, `streamSliceIndex`.
- **Configurable contexts** – build custom contexts with your own comparison logic and block sizes.
- **Immutable & persistent** – structural sharing for cheap copies, undo/redo, and change detection.
Try Rimbu (including `@rimbu/sorted`) live in the browser using the
[Rimbu Sandbox on CodeSandbox](https://codesandbox.io/s/github/vitoke/rimbu-sandbox/tree/main?previewwindow=console&view=split&editorsize=65&moduleview=1&module=/src/index.ts).
---
## Quick Start
```ts
import { SortedMap, SortedSet } from '@rimbu/sorted';
// SortedMap: keys are kept in sorted order
const m = SortedMap.of(['b', 2], ['d', 4], ['a', 1], ['c', 3]).asNormal(); // convert to a normal (possibly empty) view
console.log(m.toArray());
// => [['a', 1], ['b', 2], ['c', 3], ['d', 4]]
// SortedSet: values are kept in sorted order
const s = SortedSet.of('b', 'd', 'a', 'c').asNormal();
console.log(s.toArray());
// => ['a', 'b', 'c', 'd']
```
See the [Map docs](https://rimbu.org/docs/collections/map),
[Set docs](https://rimbu.org/docs/collections/set), and the
[sorted API reference](https://rimbu.org/api/rimbu/sorted) for full details.
---
## Core Concepts & Types
### Exported Types
From the main entry:
| Name | Description |
| -------------------------- | --------------------------------------------------------------------------------------------------- |
| `SortedMap<K, V>` | Immutable, type‑invariant map whose keys are kept in sorted order. |
| `SortedMap.NonEmpty<K, V>` | Non‑empty refinement of `SortedMap<K, V>` with stronger guarantees. |
| `SortedMap.Context<UK>` | Factory/context for creating `SortedMap` instances with a given key comparator and tree parameters. |
| `SortedMap.Builder<K, V>` | Mutable builder for efficiently constructing or mutating a `SortedMap` before freezing it. |
| `SortedSet<T>` | Immutable, type‑invariant set of values kept in sorted order. |
| `SortedSet.NonEmpty<T>` | Non‑empty refinement of `SortedSet<T>`. |
| `SortedSet.Context<UT>` | Factory/context for creating `SortedSet` instances with a given comparator and tree parameters. |
| `SortedSet.Builder<T>` | Mutable builder for efficiently constructing or mutating a `SortedSet` before freezing it. |
You can import them via:
```ts
import { SortedMap, SortedSet } from '@rimbu/sorted';
// or more granular:
// import { SortedMap } from '@rimbu/sorted/map';
// import { SortedSet } from '@rimbu/sorted/set';
```
---
## Working with `SortedMap`
```ts
import { SortedMap } from '@rimbu/sorted';
// Construction
const empty = SortedMap.empty<string, number>();
const fromEntries = SortedMap.of(
['b', 2],
['d', 4],
['a', 1],
['c', 3]
).asNormal();
// Size & emptiness
empty.isEmpty; // true
fromEntries.size; // 4
// Lookups
fromEntries.hasKey('b'); // true
fromEntries.get('c'); // 3
// Sorted access helpers
fromEntries.min(); // ['a', 1]
fromEntries.maxKey(); // 'd'
fromEntries.getAtIndex(1); // ['b', 2]
fromEntries.getValueAtIndex(-1); // 4 (last value)
// Range queries (by key and by index)
fromEntries.streamRange({ start: 'b', end: 'c' }).toArray(); // [['b', 2], ['c', 3]]
fromEntries.sliceIndex({ start: 1, amount: 2 }).toArray(); // [['b', 2], ['c', 3]]
```
See the [SortedMap API docs](https://rimbu.org/api/rimbu/sorted/map/SortedMap/interface)
for all operations (including builders, contexts, and more advanced methods).
---
## Working with `SortedSet`
```ts
import { SortedSet } from '@rimbu/sorted';
// Construction
const empty = SortedSet.empty<string>();
const values = SortedSet.of('b', 'd', 'a', 'c').asNormal();
// Basic queries
values.has('b'); // true
values.min(); // 'a'
values.max(); // 'd'
// Index‑based access
values.getAtIndex(1); // 'b'
values.getAtIndex(-1); // 'd'
// Range queries
values.streamRange({ start: 'b', end: 'c' }).toArray(); // ['b', 'c']
values.sliceIndex({ start: 1, amount: 2 }).toArray(); // ['b', 'c']
```
See the [SortedSet API docs](https://rimbu.org/api/rimbu/sorted/set/SortedSet/interface)
for the full, strongly‑typed surface.
---
## Custom Comparators & Contexts
By default, `SortedMap` / `SortedSet` use a generic comparator that works well
for most values. For specialised ordering you can create your own context:
```ts
import { SortedSet } from '@rimbu/sorted';
// Comparator compatible with the Comp<UT> interface
const caseInsensitiveStringComp = {
isComparable: (value: unknown): value is string => typeof value === 'string',
compare: (a: string, b: string): number =>
a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()),
};
const ciSetContext = SortedSet.createContext<string>({
comp: caseInsensitiveStringComp,
// blockSizeBits (optional) controls the underlying tree node size, default 5
});
const ciSet = ciSetContext.of('a', 'B', 'c');
ciSet.has('b'); // true (case‑insensitive)
```
You can do the same for `SortedMap` by providing a key comparator.
Contexts also expose `builder()` / `createBuilder()` for efficient bulk updates.
---
## Installation
### Node / Bun / npm / Yarn
```sh
npm install @rimbu/sorted
# or
yarn add @rimbu/sorted
# or
bun add @rimbu/sorted
# or
deno add npm:@rimbu/sorted
```
### Browser / ESM
`@rimbu/sorted` ships both **ESM** and **CJS** builds. Use it with any modern bundler
(Vite, Webpack, esbuild, Bun, etc.) or directly in Node ESM projects.
---
## FAQ
**Q: How is `SortedMap` different from a regular `Map`?**
`SortedMap` maintains its keys in sorted order rather than insertion order, and
adds index‑based access (`getAtIndex`, `take`, `sliceIndex`, etc.) and range‑based
streaming over keys.
**Q: Are these collections mutable?**
No. All updates return new instances; existing ones remain unchanged and can be safely
shared across your application.
**Q: What is a “context” and when should I use it?**
A context (`SortedMap.Context`, `SortedSet.Context`) encapsulates the comparator and
tree configuration. Use custom contexts when you need non‑default ordering or want
to tune block sizes for your workload.
**Q: Do I always need a custom comparator?**
No. For most use cases the default comparator is sufficient; custom comparators are
useful when domain‑specific ordering (case‑insensitive strings, locale‑aware sorting,
domain‑specific ranks, etc.) is required.
---
## Ecosystem & Integration
- Part of the broader **Rimbu** collection ecosystem – interoperates with `@rimbu/hashed`,
`@rimbu/ordered`, `@rimbu/collection-types`, and `@rimbu/stream`.
- Ideal for modelling **ordered indexes**, **time‑series views**, **leaderboards**, or
any domain where sorted iteration and range queries matter.
- Works seamlessly with other Rimbu collections and utilities for building rich, immutable data models.
Explore more at the [Rimbu documentation](https://rimbu.org) and the
[sorted API docs](https://rimbu.org/api/rimbu/sorted).
---
## Contributing
We welcome contributions! See the
[Contributing guide](https://github.com/rimbu-org/rimbu/blob/main/CONTRIBUTING.md) for details.
<img src="https://contrib.rocks/image?repo=rimbu-org/rimbu" alt="Contributors" />
_Made with [contributors-img](https://contrib.rocks)._
---
## License
MIT © Rimbu contributors. See [LICENSE](./LICENSE) for details.
---
## Attributions
Created and maintained by [Arvid Nicolaas](https://github.com/vitoke). Logo © Rimbu.