data-structure-typed

repository·main·Indexed 41 days ago

https://github.com/zrwusa/data-structure-typed

A production-ready, zero-dependency TypeScript library providing type-safe implementations of advanced data structures including Heap, Deque, Trie, Graph, Red-Black Tree, TreeMap, and TreeSet. Version 2.6.3 offers high-performance alternatives to native Array and Map, featuring O(log n) sorted lookups and order-statistic support (getRank, getByRank, rangeByRank) for tree-based structures.

Tokens
118.3K
Snippets
315
Records
434
Agent score
49%

What's inside data-structure-typed

  1. Overview of data-structure-typed

    main

    What is data-structure-typed?

    data-structure-typed is a library providing a comprehensive set of specialized data structures for JavaScript and TypeScript. It aims to fill the gap in the JS/TS standard library by offering high-performance, standardized implementations of structures that are typically missing or inefficiently simulated using built-in Array or Map objects.

    Key Problem Solved: Efficient Alternatives to Array/Map

    In standard JS/TS, developers often use Array to simulate other structures, which can lead to performance bottlenecks:

    • Queue/Deque: Using Array.shift() results in $O(n)$ time complexity. This library provides Queue and Deque with $O(1)$ enqueue and dequeue operations.
    • HashMap: The built-in Map is a LinkedHashMap (preserving insertion order), which can be slower than a pure HashMap. This library provides a dedicated HashMap for algorithm acceleration.

    Supported Data Structures

    The library includes several advanced structures:

    • Heap / Priority Queue: Supports $O(\log n)$ insertion, deletion, and search, with $O(1)$ access to the extremum (min/max).
    • Red Black Tree: A balanced binary search tree providing optimal performance for CRUD operations.
    • Linked List: Useful for efficient insertions or deletions at a known index within an ordered set.
    • Trie: Optimized for high-speed string searching and efficient string storage.
    • Graph: For modeling complex relationships between nodes.
    • Stack: While Array can be used, this library provides a formal implementation.
  2. Overview of data-structure-typed library

    main

    data-structure-typed is a comprehensive TypeScript library providing over 20 production-grade data structures with guaranteed time and space complexities. It is designed for high performance, type safety, and framework independence (working in Node.js, browsers, and edge runtimes).

    Key Design Goals

    • Unified API: Consistent interfaces for linear structures (e.g., push, pop, shift, unshift).
    • Type Safety: Full TypeScript generics and strict typing.
    • Performance Guarantees: Optimized complexities like $O(\log n)$ for tree structures and $O(1)$ for deque operations.
    • Zero Friction: All structures implement the Iterator protocol.
    • Production Ready: Comprehensive error handling and edge case coverage.
  3. Understand performance characteristics of data-structure-typed

    main

    When choosing a data structure from data-structure-typed, consider the Big O complexity and the specific performance gains over native JavaScript types.

    Key Performance Gains

    • Deque vs Array: Using a Deque is up to 484x faster than Array.shift() when handling 100K elements due to $O(1)$ head/tail operations compared to Array's $O(n)$.
    • RedBlackTree vs Array: In workloads requiring repeated "update + resort", RedBlackTree can be 40x–308x faster than using a native Array.

    Complexity Reference

    StructureAccessSearchInsertDeleteBest For
    ArrayO(1)O(n)O(n)O(n)Random access
    LinkedListO(n)O(n)O(1)*O(1)*If you have a pointer
    Stack--O(1)O(1)LIFO, undo/redo
    Queue--O(1)O(1)FIFO, message queues
    Deque--O(1)O(1)Head/tail ops
    BSTO(log n)O(log n)O(log n)O(log n)Sorted if balanced
    RedBlackTreeO(log n)O(log n)O(log n)O(log n)Guaranteed sorted
    AVL TreeO(log n)O(log n)O(log n)O(log n)Max search speed
    HeapO(n)O(n)O(log n)O(log n)Priority queue
    TrieN/AO(m)O(m)O(m)Prefix search

    Note: LinkedList insert/delete is $O(1)$ if the pointer to the node is already known.

  4. Supported Data Structures and Complexity

    main

    The data-structure-typed library provides over 20 production-ready data structures categorized into Linear, Tree, Priority Queue, and Specialized structures.

    Linear Structures

    • Stack, Queue, Deque: O(1) for all operations.
    • Singly/Doubly Linked List: O(n) average access, O(1) insertion (with pointer access).

    Tree Structures

    • BST: O(log n) average, O(n) worst case.
    • RedBlackTree, AVLTree, TreeMultiMap: Guaranteed O(log n) for operations.

    Priority Queues & Heaps

    • Heap, MinHeap, MaxHeap, PriorityQueue: O(log n) for add/remove operations.

    Specialized Structures

    • Trie: O(m+k) for prefix search.
    • HashMap: O(1) average lookup.
    • Graph, DirectedGraph, UndirectedGraph: O(V+E) for traversal/search.
    • SegmentTree, BinaryIndexedTree: O(log n) for range/cumulative queries (Beta).
  5. Performance Overview of data-structure-typed

    main

    Understanding the performance characteristics of data-structure-typed helps in choosing the right structure for your specific workload. When comparing results against C++ or other non-JS implementations, note that differences are often due to the JavaScript runtime (Garbage Collection, boxed numbers, dynamic dispatch) rather than the library itself. For practical decision-making, focus on comparisons within the JavaScript ecosystem.

    Key Performance Highlights

    • Deque vs Array: For 100,000 elements, Deque is up to 484x faster than Array.shift().
    • RedBlackTree vs Array: In workloads involving repeated "update + re-sort" operations, RedBlackTree is 40x–308x faster than Array.
    • Complexity Guarantees:
      • All balanced tree operations are guaranteed O(log n).
      • Deque head and tail operations are guaranteed O(1).
  6. Performance Summary and Key Benchmarks

    main

    When choosing a data structure in data-structure-typed, consider the following performance characteristics compared to native JavaScript implementations:

    • Deque vs Array: A Deque is up to 484x faster than Array.shift() when handling 100K elements.
    • RedBlackTree vs Array: For workloads involving repeated "update + resort" operations, RedBlackTree is 40x–308x faster than using an Array.
    • Complexity Guarantees:
      • All balanced tree operations provide O(log n) complexity.
      • Deque head and tail operations provide O(1) complexity.

    Note on Cross-Language Comparisons: Benchmarks comparing JavaScript structures to C++ (like js-sdsl) should be treated as directional. Differences are often due to the JavaScript runtime (GC, boxed numbers, dynamic dispatch) rather than the library implementation itself. For practical decision-making, prioritize within-JS comparisons.

  7. What is data-structure-typed and when to use it

    main

    Overview

    data-structure-typed is a high-performance data structures library for JavaScript and TypeScript designed to fill gaps in the native language standard library.

    When to use it

    You should use this library instead of native JavaScript structures when:

    1. Performance is critical: When using Array.shift() for queue operations, which is O(n), you should switch to the library's Queue or Deque to achieve O(1) complexity.
    2. Ordering is not required: If you need a high-performance HashMap and do not require the insertion-order preservation (and associated overhead) of the native JavaScript Map.
    3. Missing structures are needed: When your algorithm requires structures not natively available in JS/TS, such as:
      • Heap / Priority Queue: For $O(\log n)$ insertion/deletion and $O(1)$ access to min/max values.
      • Red-Black Tree: For balanced binary search tree performance in lookups, insertions, and deletions.
      • Graph and AVL Tree: For complex relational or balanced tree operations.

    Key Advantages

    • Superior Performance: Implementations are optimized to surpass native JS structures in specific use cases and aim for parity with C++ or Java standard libraries.
    • Standardized API: All structures follow a uniform interface including standard methods like forEach, filter, map, reduce, find, and clear.
  8. MapMode semantics for `undefined` values and `set()`

    main

    When using MapMode, the internal node is the single source of truth for values. This introduces a specific semantic behavior regarding undefined:

    1. Presence vs. Value: MapMode distinguishes between a missing key and a key that exists with an undefined value. has(key) will return true even if the value is undefined because the index tracks the node, not the value.
    2. set(key, undefined): Calling set with undefined will overwrite the existing value on that node. The key remains in the _store index, and get(key) will return undefined.
    3. TreeSet Compatibility: This design ensures that TreeSet (where values are always undefined) functions correctly, as the index tracks the existence of the key regardless of the value.

    Warning: To maintain MapMode correctness, keys must be treated as logically immutable. Mutating a node.key externally will break the _store index mapping.

  9. Use TreeMultiSet (Ordered MultiSet/Bag)

    main

    A TreeMultiSet is an ordered multiset (bag) where each element x has a non-negative integer count c(x). It is backed by a RedBlackTree.

    Key Concepts:

    • Size vs Distinct Size: size returns the total number of occurrences ($\sum$ counts), while distinctSize returns the number of unique keys.
    • Iteration: Default iteration is expanded, meaning it yields each element c(x) times.
    • Navigable Methods: Methods like ceiling, floor, higher, and lower return the element K (or undefined).
    • Polling: pollFirst() and pollLast() perform a deleteAll on the respective extreme element and return it.
    const scores = new TreeMultiSet([100, 95, 100, 87, 95, 100]);
    // { 87: 1, 95: 2, 100: 3 }
    
    scores.size;          // 6 (total)
    scores.distinctSize;  // 3 (unique)
    scores.count(100);    // 3
    scores.first();       // 87
    scores.last();        // 100
    scores.ceiling(90);   // 95
    
    // Functional operations
    const highScores = scores.filter((k, c) => k >= 95);
    // { 95: 2, 100: 3 }
    
    const doubled = scores.map((k, c) => [k * 2, c]);
    // { 174: 1, 190: 2, 200: 3 }
  10. Compare Map vs TreeMap for decision making

    main

    Choose between the native Map and TreeMap based on your performance and feature requirements:

    FeatureMapTreeMap
    Sorted iteration❌✅
    floor / ceiling❌✅
    Range queries❌✅
    getRank / getByRank❌✅
    Insert / lookupO(1) avgO(log n)
    MemoryLowerHigher

    Use Map when you only need key-value lookup and don't care about order. Use TreeMap when you need sorted keys, range queries, floor/ceiling, or rank operations.

  11. Use method chaining and terminal operations

    main

    The library supports functional-style method chaining.

    Chainable Operations

    Methods like .filter(predicate) and .map(mapper) return this (the same structure type), allowing you to chain multiple operations together.

    Terminal Operations

    Operations that return a final value instead of the structure itself are considered terminal and break the chain. Examples include:

    • .reduce(reducer, initial): Returns the accumulated value.
    • .toArray(): Returns T[].
    • .find(pred): Returns T | undefined.
    • .some(pred): Returns boolean.
    const tree = new RedBlackTree([[1, 'a'], [2, 'b']]);
    
    const result = tree
      .filter((v, k) => k > 1)    // Still RedBlackTree
      .map((v, k) => [k, v.toUpperCase()]);  // Still RedBlackTree
    
    // Access tree methods still available
    result.set(3, 'C');
    
    // Terminal operation example
    const array = tree.toArray(); // Returns T[]
  12. Map raw objects to data structures using configuration

    main

    Instead of manually mapping data before insertion, you can pass raw objects to constructors using specific configuration functions:

    • toElementFn: Extracts a specific field to be stored as the element. Used by Heap, Queue, Stack, LinkedList, and Trie.
    • toEntryFn: Splits an object into a [key, value] pair. Used by TreeMap, HashMap, and SkipList.
    • comparator: Stores the full object but uses a custom comparison logic for ordering. Used by all ordered structures.
    // toElementFn: Extract field for Heap
    const heap = new MinHeap<number, User>(users, {
      toElementFn: u => u.age
    });
    
    // toEntryFn: Split into key-value for TreeMap
    const map = new TreeMap<number, User, User>(users, {
      toEntryFn: u => [u.id, u]
    });
    
    // comparator: Sort full objects in TreeSet
    const set = new TreeSet<User>(users, {
      comparator: (a, b) => a.id - b.id
    });