UNPKG

@rimbu/sorted

Version:

Immutable SortedMap and SortedSet implementations for TypeScript

299 lines (211 loc) 10.3 kB
<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"> [![npm version](https://badge.fury.io/js/@rimbu%2Fsorted.svg)](https://www.npmjs.com/package/@rimbu/sorted) ![License](https://img.shields.io/github/license/rimbu-org/rimbu) ![Types Included](https://img.shields.io/badge/TypeScript-ready-blue) ![Node](https://img.shields.io/badge/Node-18+-6DA55F?logo=node.js&logoColor=white) ![Bun](https://img.shields.io/badge/Bun-%23000000.svg) ![ESM + CJS](https://img.shields.io/badge/modules-ESM%20%2B%20CJS-informational) </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.