data-structure-typed
π Uniform API: Coding feels like home.
Donβt learn new APIs. Just use
push,pop,map,filter, andreduceeverywhere.
β‘ High Performance: Speed without compromise.
Benchmarks prove it. We outperform native implementations in critical scenarios.
π‘οΈ Type Safe: TypeScript first, always.
No more
any. Enjoy full generics and strict type checking out of the box.
β¨ Zero Friction: It just plays nice.
Works with everything. Spread it
[...], loop itfor..of, or convert it instantly.
Installation and Usage
pnpm
pnpm add data-structure-typed
Playground
[Node.js JavaScript](https://stackblitz.com/β¦
data-structure-typed
π Uniform API: Coding feels like home.
Donβt learn new APIs. Just use
push,pop,map,filter, andreduceeverywhere.
β‘ High Performance: Speed without compromise.
Benchmarks prove it. We outperform native implementations in critical scenarios.
π‘οΈ Type Safe: TypeScript first, always.
No more
any. Enjoy full generics and strict type checking out of the box.
β¨ Zero Friction: It just plays nice.
Works with everything. Spread it
[...], loop itfor..of, or convert it instantly.
Installation and Usage
pnpm
pnpm add data-structure-typed
Playground
npm
npm i data-structure-typed --save
yarn
yarn add data-structure-typed
Individual Data Structure Installation
If you only want to use a specific data structure independently:
npm i heap-typed --save
npm i bst-typed --save
npm i red-black-tree-typed --save
π Quick Navigation
Choose your learning path based on your needs:
π€ Iβm New Here
- β±οΈ 3 Minutes to Productivity: Quick Start
- π Full Understanding: Why data-structure-typed?
- π» Show Me Code: Code Snippets
π€ Not Sure Which Data Structure to Use?
- π― Decision Guide: Choose Your Data Structure
- π Data Structures Overview: Complete List
π Migrating from Native JavaScript?
- π Migration Guide: From Array to Specialized Structures
- π Integration Examples: React, Express, Nest.js
π Performance-Focused?
- β‘ Performance Data: Benchmark Results
- π Real Cases: 5 Production Code Examples
π’ Enterprise Integration?
- π Framework Integration: React, Vue, Express, Nest.js, Next.js
- π οΈ Multi-Environment: CDN/CommonJS/ESModule/TypeScript
Quick Verification
import {
Heap, Graph, Queue, Deque, PriorityQueue, BST, Trie, DoublyLinkedList,
AVLTree, SinglyLinkedList, DirectedGraph, RedBlackTree, TreeMultiMap,
DirectedVertex, Stack, AVLTreeNode
} from 'data-structure-typed';
// Quick test
const tree = new RedBlackTree([5, 2, 8, 1, 9]);
console.log([...tree.keys()]); // [1, 2, 5, 8, 9]
Quick Start: 3 Minutes to Productivity
Scenario 1: High-Performance Queue
Problem: You need to frequently remove elements from the front of a list.
β This is slow with Array:
const queue = [1, 2, 3, 4, 5];
queue.shift(); // O(n) - Reindexes all remaining elements!
β This is fast with Deque:
import { Deque } from 'data-structure-typed';
const deque = new Deque([1, 2, 3, 4, 5]);
deque.shift(); // O(1) - Just moves a pointer
deque.print(); // [2, 3, 4, 5]
Scenario 2: Sorted Data with Fast Lookups
Problem: You need to maintain sorted data and query it efficiently.
β Array requires manual sorting:
const arr = [5, 2, 8, 1, 9];
arr.includes(3); // O(n) - Must check every element
β RedBlackTree maintains sorted order automatically:
import { RedBlackTree } from 'data-structure-typed';
const tree = new RedBlackTree([5, 2, 8, 1, 9]);
tree.has(3); // O(log n) - Logarithmic search
// Iterating tree is already sorted
for (const [key] of tree) {
console.log(key); // 1, 2, 5, 8, 9 (automatically sorted!)
}
Scenario 3: Priority Queue
Problem: Process items by priority, not insertion order.
β Array requires re-sorting:
const tasks = [];
function addTask(task) {
tasks.push(task);
tasks.sort((a, b) => b.priority - a.priority); // O(n log n) every time!
}
β PriorityQueue maintains priority automatically:
import { MaxPriorityQueue } from 'data-structure-typed';
const pq = new MaxPriorityQueue([], {
comparator: (a, b) => b.priority - a.priority,
});
function addTask(task) {
pq.add(task); // O(log n)
}
const nextTask = pq.poll(); // Always get highest priority
Scenario 4: Prefix Matching (Autocomplete)
Problem: Fast prefix searching in large dictionaries.
import { Trie } from 'data-structure-typed';
const dictionary = new Trie(['apple', 'app', 'apply', 'application']);
const suggestions = dictionary.getWords('appl');
// Returns: ['apple', 'apply', 'application']
// Time: O(m + k) where m is prefix length, k is results
Scenario 5: Graph Algorithms
Problem: Pathfinding or cycle detection.
import { DirectedGraph } from 'data-structure-typed';
const graph = new DirectedGraph();
graph.addVertex('A');
graph.addVertex('B');
graph.addEdge('A', 'B', 1);
const { minDist, minPath } = graph.dijkstra('A', 'B', true, true);
Why data-structure-typed?
All data structures in JavaScript should work like native Array. No more API chaos. No more conversions. No more frustration.
Three Pain Points Every Developer Faces
1οΈβ£ Performance Wall: When Operations Become Bottlenecks
β The Problem: Array.shift() is O(n):
const queue = [1, 2, 3, ..., 100000
]
;
for (let i = 0; i < 100000; i++) {
queue.shift(); // Reindexes all remaining elements!
}
// Time: 2829ms for 100K items β
// Impact: Timeout, failed test
β The Solution: Use Deque:
import { Deque } from 'data-structure-typed';
const deque = new Deque([1, 2, 3, ..., 100000
])
;
for (let i = 0; i < 100000; i++) {
deque.shift(); // O(1) - Just moves a pointer
}
// Time: 5.83ms for 100K items β
// Speedup: 484x faster! π
Real-world impact:
- Competitive programming: TLE β β AC β
- Real-time systems: P99 latency 500ms β β 5ms β
- Message queues: Throughput 100/sec β β 10,000/sec β
2οΈβ£ API Chaos: Learning a New API for Every Data Structure
β Different libraries use different APIs:
// Library 1: Uses offer/poll (Java-style)
queue.offer(item);
queue.poll();
// Library 2: Uses push/shift
queue.push(item);
queue.shift();
// Library 3: Uses enqueue/dequeue
queue.enqueue(item);
queue.dequeue();
β Our library uses consistent APIs everywhere:
In java.utils, you need to memorize different methods for Queue, Deque, LinkedList:
| Java ArrayList | Java Queue | Java ArrayDeque | Java LinkedList |
|---|---|---|---|
| add | offer | push | push |
| remove | poll | removeLast | removeLast |
| remove | poll | removeFirst | removeFirst |
| add(0, element) | offerFirst | unshift | unshift |
In data-structure-typed, you only need to remember four methods: push, pop, shift, and unshift for all linear structures (Queue, Deque, DoublyLinkedList, SinglyLinkedList, Stack).
// ALL linear structures use THE SAME 4 methods
const deque = new Deque([1, 2, 3]);
const queue = new Queue([1, 2, 3]);
const stack = new Stack([1, 2, 3]);
const list = new DoublyLinkedList([1, 2, 3]);
// They ALL support:
structure.push(item); // Add to end
structure.pop(); // Remove from end
structure.shift(); // Remove from start
structure.unshift(item); // Add to start
// And ALL support Array's advanced methods
structure.map((_value, key) => key * 2);
structure.filter((_value, key) => key > 5);
structure.reduce((acc, _value, key) => acc + key, 0);
3οΈβ£ Conversion Hell: Bouncing Data Between Structures
β The Painful Way:
const scores = [95, 23, 67, 89, 12, 45];
const tree = new SomeTreeLibrary(scores);
const filtered = tree.toArray().filter(s => s > 50); // Convert to array
const mapped = filtered.map(s => s * 2); // Another conversion
// Multiple conversions, lost benefits, easy to make mistakes
β The Clean Way:
const tree = new RedBlackTree(scores);
// All methods work DIRECTLY on the tree
const result = tree
.filter((_value, key) => key > 50) // Direct
.map((_value, key) => [key, key * 1]) // Direct
.reduce((acc, value) => acc + (value ?? 0), 0); // Direct
// β
Zero conversions, tree structure maintained
π Seamless Interoperability: Iterator Protocol Everywhere
The Hidden Superpower
Every single data structure implements the Iterator protocol:
- β
Spread operator:
[...tree] - β
for...of loops:
for (const item of tree) - β
Destructuring:
const [a, b, c] = tree - β
Array.from():
Array.from(tree) - β
Set/Map constructors:
new Set(tree)
Iterator Support Comparison
| Feature | Array | Map | Set | Other Lib | data-structure-typed |
|---|---|---|---|---|---|
| Spread operator | β | β/β οΈ | β | β/β οΈ | β |
| for...of loop | β | β | β | β/β οΈ | β |
| Destructuring | β | β | β | β | β |
| Array.from() | β | β/β οΈ | β | β/β οΈ | β |
| Set constructor | β | β | β | β | β |
| Full Integration | β | β οΈ | β οΈ | β οΈ | β |
Live Examples: Zero Friction Conversions
Example 1: Array to Tree to Array
const array = [64, 34, 25, 12, 22, 11, 90];
const rbTree = new RedBlackTree(array);
const sorted = [...rbTree.keys()];
console.log(sorted); // [11, 12, 22, 25, 34, 64, 90] β
Example 2: Extract Keys and Values
const rbTree = new RedBlackTree([
[1, 'Alice'],
[2, 'Bob'],
[3, 'Charlie']
]);
const allKeys = [...rbTree.keys()]; // [1, 2, 3]
const allValues = [...rbTree.values()]; // ['Alice', 'Bob', 'Charlie']
Example 3: for...of on Any Structure
const tree = new RedBlackTree(entries);
const deque = new Deque(items);
const heap = new MaxHeap(items);
for (const entry of tree) console.log(entry);
for (const item of deque) console.log(item);
for (const item of heap) console.log(item);
π All Array Methods Work Everywhere
The Biggest Developer Joy: Array Methods, Everywhere
You know these methods. You use them every day. They work on every data structure:
Chain on Tree
const rbTree = new RedBlackTree([
[1, { name: 'Alice', age: 25 }],
[2, { name: 'Bob', age: 30 }],
[3, { name: 'Charlie', age: 28 }],
]);
const result = rbTree
.filter((value, _key) => (value?.age ?? 0) > 26)
.map((value, key) => [key, { ...value, id: key }])
.reduce((sum, value) => sum + (value?.age ?? 0), 0);
console.log(result); // 58 β
Chain on Heap
const minHeap = new Heap([
{ priority: 5, task: 'Email' },
{ priority: 3, task: 'Chat' },
{ priority: 8, task: 'Alert' },
], { comparator: (a, b) => a.priority - b.priority });
const urgent = minHeap
.filter((value, _key) => value.priority > 4)
.map((value, _key) => value.task);
urgent.print(); // ['Alert', 'Email'] β
Chain on Deque
const deque = new Deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
const stats = {
even: deque.filter((value, _key) => value % 2 === 0).toArray(),
squared: deque.map((value, _key) => value * value).toArray(),
hasLarge: deque.some((value, _key) => value > 8),
sum: deque.reduce((acc, value, _key) => acc + value, 0),
};
Supported Methods Across All Structures
| Method | BinaryTrees | Heap | Deque | Graph | LinkedList |
|---|---|---|---|---|---|
| map | β | β | β | β | β |
| filter | β | β | β | β | β |
| reduce | β | β | β | β | β |
| find | β | β | β | β | β |
| some/every | β | β | β | β | β |
| keys/values | β | β | β | β | β |
| forEach | β | β | β | β | β |
Why Not Just Use Native JavaScript?
Case 1: Map Doesnβt Maintain Sorted Order
β Map iteration is insertion order, not key order:
const map = new Map([[5, 'E'], [2, 'B'], [8, 'H'], [1, 'A']]);
for (const [key, value] of map) {
console.log(key); // 5, 2, 8, 1 (insertion order)
}
β RedBlackTree maintains sorted order automatically:
const tree = new RedBlackTree([[5, 'E'], [2, 'B'], [8, 'H'], [1, 'A']]);
for (const [key, value] of tree) {
console.log(key); // 1, 2, 5, 8 (key order) β
}
Case 2: Array.shift is Too Slow
β Array.shift is O(n):
const queue = [];
for (let i = 0; i < 100000; i++) queue.push(i);
for (let i = 0; i < 100000; i++) queue.shift();
// Time: 2829ms β
β Deque.shift is O(1):
const deque = new Deque();
for (let i = 0; i < 100000; i++) deque.push(i);
for (let i = 0; i < 100000; i++) deque.shift();
// Time: 5.83ms β
Case 3: Maintaining Priority is Manual
β Array requires re-sorting O(n log n):
const tasks = [];
function addTask(task) {
tasks.push(task);
tasks.sort((a, b) => b.priority - a.priority);
}
β PriorityQueue maintains priority O(log n):
const pq = new MaxPriorityQueue();
function addTask(task) {
pq.add(task); // O(log n)
}
Case 4: Range Queries are Tedious
β Array.filter is O(n):
const prices = [10, 45, 23, 67, 89, 12, 54, 33, 78];
const inRange = prices.filter(p => p >= 30 && p <= 70);
β RedBlackTree range queries are O(log n + k):
const tree = new RedBlackTree(prices.map((p, i) => [p, i]));
const inRange = tree.filter((_value, p) => p >= 30 && p <= 70);
Case 5: Prefix Matching is Tedious
β Array.filter is O(n*m):
const words = ['apple', 'app', 'apply', 'application'];
const matches = words.filter(w => w.startsWith('app'));
// For 1M words: checks 1M words β
β Trie prefix matching is O(m + k):
const trie = new Trie(words);
const matches = trie.getWords('appl');
// O(5 + 4) = 9 operations β
π Performance Comparison
Performance surpasses that of native JS/TS
| Method | Time Taken | Data Scale | Belongs To | Big O |
|---|---|---|---|---|
| Queue.push & shift | 5.83 ms | 100K | Ours | O(1) |
| Array.push & shift | 2829.59 ms | 100K | Native JS | O(n) |
| Deque.unshift & shift | 2.44 ms | 100K | Ours | O(1) |
| Array.unshift & shift | 4750.37 ms | 100K | Native JS | O(n) |
| HashMap.set | 122.51 ms | 1M | Ours | O(1) |
| Map.set | 223.80 ms | 1M | Native JS | O(1) |
| Set.add | 185.06 ms | 1M | Native JS | O(1) |
Benchmark
MacBook Pro (15-inch, 2018)
Processor 2.2 GHz 6-Core Intel Core i7
Memory 16 GB 2400 MHz DDR4
Graphics Radeon Pro 555X 4 GB
Intel UHD Graphics 630 1536 MB
macOS Sequoia 15.7.2
Performance & Runtime Compatibility
red-black-tree
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000,000 add | 410.34 | 0.41 | 0.01 |
| 1,000,000 get | 5.20 | 0.01 | 8.16e-5 |
| 1,000,000 iterator | 154.25 | 0.15 | 0.02 |
| CPT 1,000,000 add | 656.43 | 0.66 | 0.00 |
| CPT 1,000,000 add | 684.17 | 0.68 | 0.01 |
queue
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000,000 push | 26.97 | 0.03 | 0.00 |
| 100,000 push & shift | 2.87 | 0.00 | 2.71e-4 |
| Native JS Array 100,000 push & shift | 1120.94 | 1.12 | 0.20 |
deque
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000,000 push | 8.75 | 0.01 | 6.99e-4 |
| 1,000,000 push & pop | 12.95 | 0.01 | 4.21e-4 |
| 1,000,000 push & shift | 13.73 | 0.01 | 4.53e-4 |
| 100,000 push & shift | 1.36 | 0.00 | 5.42e-5 |
| Native JS Array 100,000 push & shift | 1167.06 | 1.17 | 0.26 |
| 100,000 unshift & shift | 1.31 | 0.00 | 4.73e-5 |
| Native JS Array 100,000 unshift & shift | 1911.47 | 1.91 | 0.02 |
heap
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 100,000 add | 4.60 | 0.00 | 1.07e-4 |
| 100,000 add & poll | 16.96 | 0.02 | 3.45e-4 |
avl-tree
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 100,000 add randomly | 324.51 | 0.32 | 0.01 |
| 100,000 add | 299.76 | 0.30 | 0.02 |
| 100,000 get | 0.26 | 2.58e-4 | 3.65e-6 |
| 100,000 getNode | 169.33 | 0.17 | 0.00 |
| 100,000 iterator | 14.43 | 0.01 | 0.00 |
| 100,000 add & delete orderly | 434.44 | 0.43 | 0.01 |
| 100,000 add & delete randomly | 541.78 | 0.54 | 0.01 |
hash-map
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000,000 set | 43.23 | 0.04 | 0.01 |
| Native JS Map 1,000,000 set | 147.12 | 0.15 | 0.01 |
| Native JS Set 1,000,000 add | 116.18 | 0.12 | 0.01 |
| 1,000,000 set & get | 46.39 | 0.05 | 0.01 |
| Native JS Map 1,000,000 set & get | 196.92 | 0.20 | 0.01 |
| Native JS Set 1,000,000 add & has | 163.92 | 0.16 | 0.01 |
| 1,000,000 ObjKey set & get | 243.36 | 0.24 | 0.03 |
| Native JS Map 1,000,000 ObjKey set & get | 211.66 | 0.21 | 0.02 |
| Native JS Set 1,000,000 ObjKey add & has | 196.57 | 0.20 | 0.01 |
directed-graph
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000 addVertex | 0.05 | 4.60e-5 | 6.59e-7 |
| 1,000 addEdge | 3.02 | 0.00 | 2.85e-4 |
| 1,000 getVertex | 0.04 | 3.77e-5 | 4.66e-7 |
| 1,000 getEdge | 41.48 | 0.04 | 0.01 |
| tarjan | 240.33 | 0.24 | 0.01 |
| topologicalSort | 195.62 | 0.20 | 0.01 |
trie
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 100,000 push | 27.15 | 0.03 | 6.61e-4 |
| 100,000 getWords | 41.18 | 0.04 | 0.00 |
stack
| test name | time taken (ms) | sample mean (secs) | sample deviation |
|---|---|---|---|
| 1,000,000 push | 25.21 | 0.03 | 0.00 |
| 1,000,000 push & pop | 29.12 | 0.03 | 0.00 |
π Plain Language Explanations
For those who love understanding concepts through metaphors:
| Data Structure | Plain Language Definition | Example |
|---|---|---|
| Linked List (Singly Linked List) | A line of bunnies, where each bunny holds the tail of the bunny in front of it (each bunny only knows the name of the bunny behind it). You want to find a bunny named Pablo, and you have to start searching from the first bunny. If itβs not Pablo, you continue following that bunnyβs tail to the next one. So, you might need to search n times to find Pablo (O(n) time complexity). If you want to insert a bunny named Remi between Pablo and Vicky, itβs very simple. You just need to let Vicky release Pabloβs tail, let Remi hold Pabloβs tail, and then let Vicky hold Remiβs tail (O(1) time complexity). | To find bunny "Pablo", start from the first bunny and follow tails until found |
| Array | A line of numbered bunnies. If you want to find the bunny named Pablo, you can directly shout out Pabloβs number 0680 (finding the element directly through array indexing, O(1) time complexity). However, if you donβt know Pabloβs number, you still need to search one by one (O(n) time complexity). Moreover, if you want to add a bunny named Vicky behind Pablo, you will need to renumber all the bunnies after Vicky (O(n) time complexity). | Finding element by index is instant, but inserting in the middle is slow |
| Queue | A line of numbered bunnies with a sticky note on the first bunny. For this line with a sticky note on the first bunny, whenever we want to remove a bunny from the front of the line, we only need to move the sticky note to the face of the next bunny without actually removing the bunny to avoid renumbering all the bunnies behind (removing from the front is also O(1) time complexity). For the tail of the line, we donβt need to worry because each new bunny added to the tail is directly given a new number (O(1) time complexity) without needing to renumber all the previous bunnies. | Process items in FIFO order, efficiently from both ends |
| Deque | A line of grouped, numbered bunnies with a sticky note on the first bunny. For this line, we manage it by groups. Each time we remove a bunny from the front of the line, we only move the sticky note to the next bunny. This way, we donβt need to renumber all the bunnies behind the first bunny each time a bunny is removed. Only when all members of a group are removed do we reassign numbers and regroup. The tail is handled similarly. This is a strategy of delaying and batching operations to offset the drawbacks of the Array data structure that requires moving all elements behind when inserting or deleting elements in the middle. | Efficient removal/insertion from both ends with batching optimization |
| Doubly Linked List | A line of bunnies where each bunny holds the tail of the bunny in front (each bunny knows the names of the two adjacent bunnies). This provides the Singly Linked List the ability to search forward, and thatβs all. For example, if you directly come to the bunny Remi in the line and ask her where Vicky is, she will say the one holding my tail behind me, and if you ask her where Pablo is, she will say right in front. | Bidirectional traversal; efficient insertion/deletion from both ends |
| Stack | A line of bunnies in a dead-end tunnel, where bunnies can only be removed from the tunnel entrance (end), and new bunnies can only be added at the entrance (end) as well. | Process items in LIFO order; undo/redo functionality |
| Binary Tree | As the name suggests, itβs a tree where each node has at most two children. When you add consecutive data such as [4, 2, 6, 1, 3, 5, 7], it will be a complete binary tree. When you add data like [4, 2, 6, null, 1, 3, null, 5, null, 7], you can specify whether any left or right child node is null, and the shape of the tree is fully controllable. | Hierarchical organization of data; expression parsing |
| Binary Search Tree (BST) | A tree-like rabbit colony composed of doubly linked lists where each rabbit has at most two tails. These rabbits are disciplined and obedient, arranged in their positions according to a certain order. The most important data structure in a binary tree (the core is that the time complexity for insertion, deletion, modification, and search is O(log n)). The data stored in a BST is structured and ordered, not in strict order like 1, 2, 3, 4, 5, but maintaining that all nodes in the left subtree are less than the node, and all nodes in the right subtree are greater than the node. This order provides O(log n) time complexity for insertion, deletion, modification, and search. Reducing O(n) to O(log n) is the most common algorithm complexity optimization in the computer field, an exponential improvement in efficiency. Itβs also the most efficient way to organize unordered data into ordered data (most sorting algorithms only maintain O(n log n)). Of course, the binary search trees we provide support organizing data in both ascending and descending order. Remember that basic BSTs do not have self-balancing capabilities, and if you sequentially add sorted data to this data structure, it will degrade into a list, thus losing the O(log n) capability. Of course, our addMany method is specially handled to prevent degradation. However, for practical applications, please use Red-black Tree or AVL Tree as much as possible, as they inherently have self-balancing functions. | Find items exponentially faster than linear search; maintains O(log n) for search/insert/delete |
| Red-Black Tree | A tree-like rabbit colony composed of doubly linked lists, where each rabbit has at most two tails. These rabbits are not only obedient but also intelligent, automatically arranging their positions in a certain order. A self-balancing binary search tree. Each node is marked with a red-black label. Ensuring that no path is more than twice as long as any other (maintaining a certain balance to improve the speed of search, addition, and deletion). | Maintains O(log n) guarantees automatically; used in Java TreeMap/TreeSet |
| AVL Tree | A tree-like rabbit colony composed of doubly linked lists, where each rabbit has at most two tails. These rabbits are not only obedient but also intelligent, automatically arranging their positions in a certain order, and they follow very strict rules. A self-balancing binary search tree. Each node is marked with a balance factor, representing the height difference between its left and right subtrees. The absolute value of the balance factor does not exceed 1 (maintaining stricter balance, which makes search efficiency higher than Red-black Tree, but insertion and deletion operations will be more complex and relatively less efficient). | Maximum search speed with strict balance; slower insertions/deletions |
| Heap | A special type of complete binary tree, often stored in an array, where the children nodes of the node at index i are at indices 2i+1 and 2i+2. Naturally, the parent node of any node is at β(iβ1)/2β. | Efficient priority queue implementation; heap sort algorithm |
| Priority Queue | Itβs actually a Heap. | Task scheduling with priorities; Dijkstraβs algorithm |
| Graph | The base class for Directed Graph and Undirected Graph, providing some common methods. | Model relationships, networks, dependencies |
| Directed Graph | A network-like bunny group where each bunny can have up to n tails (Singly Linked List). | Social networks with followers; web page links |
| Undirected Graph | A network-like bunny group where each bunny can have up to n tails (Doubly Linked List). | Facebook friendships; road networks |
π» Code Snippets: Patterns & Examples
Pattern 1: Interoperability & Iterator Conversion
import { RedBlackTree } from 'data-structure-typed';
const numbers = [6, 1, 2, 7, 5, 3, 4, 9, 8];
// Create sorted tree
const tree = new RedBlackTree(numbers);
// Convert to array (spread operator)
const sorted = [...tree];
console.log(sorted); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// Iterate with for...of
for (const item of tree) {
console.log(item);
}
// Get keys/values separately
const keys = [...tree.keys()];
const values = [...tree.values()];
// Destructuring works
const [first, second, ...rest] = tree;
// Works with native JavaScript
const json = JSON.stringify([...tree]);
const set = new Set(tree.keys());
Pattern 2: Method Chaining on Exotic Structures
β Before (Array with shift):
const queue = [1, 2, 3, 4, 5];
for (let i = 0; i < 100000; i++) {
queue.shift(); // O(n)
}
β After (Deque):
import { Deque } from 'data-structure-typed';
const deque = new Deque([1, 2, 3, 4, 5]);
for (let i = 0; i < 100000; i++) {
deque.shift(); // O(1)
}
Pattern 3: Seamless Structure Conversion
import { RedBlackTree, MaxHeap, Deque } from 'data-structure-typed';
const data = [64, 34, 25, 12, 22, 11, 90];
// Array β Tree for sorting
const tree = new RedBlackTree(data);
console.log([...tree.keys()]); // [11, 12, 22, 25, 34, 64, 90]
// Tree β Heap for priority
const heap = new MaxHeap([...tree.keys()]);
// Heap β Deque for queue operations
const deque = new Deque([...heap]);
// Back to Array for final output
const result = [...deque];
Pattern 4: Query and Analysis
Tree Example
const tree = new RedBlackTree<number, { name: string; score: number }>();
tree.add(1, { name: 'Alice', score: 95 });
tree.add(2, { name: 'Bob', score: 87 });
tree.add(3, { name: 'Charlie', score: 92 });
const totalHighScore = tree
.filter((value, _key) => (value?.score ?? 0) >= 85)
.map((value, key) => [key, value?.score ?? 0])
.reduce((sum, score) => sum + (score ?? 0), 0);
console.log(totalHighScore); // 274
Heap Example
const heap = new MaxHeap([
{ priority: 5, task: 'Email' },
{ priority: 8, task: 'Alert' },
], { comparator: (a, b) => b.priority - a.priority });
const urgentTasks = heap
.filter((value, _key) => value.priority >= 8)
.map((value, _key) => [value.priority, value.task]);
Deque Example
const deque = new Deque<number>([1, 2, 3, 4, 5]);
const evenSum = deque
.filter((value, _key) => value % 2 === 0)
.map((value, _key) => value * 2)
.reduce((sum, value) => sum + value, 0);
π CRUD Operations: Basic Usage Examples
Red Black Tree CRUD
TS
import { RedBlackTree } from 'data-structure-typed';
const rbTree = new RedBlackTree<number>();
// CREATE: Add elements
rbTree.add(11);
rbTree.add(3);
rbTree.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
// READ: Query elements
rbTree.has(6); // true
rbTree.size === 16; // true
const node6 = rbTree.getNode(6); // BSTNode
rbTree.getHeight(6) === 2; // true (height of node with key 6)
rbTree.getHeight() === 5; // true (tree height)
// UPDATE: Delete elements
rbTree.delete(6);
rbTree.get(6); // undefined
// Query after update
rbTree.getLeftMost()?.key === 1; // true
rbTree.isAVLBalanced(); // true
rbTree.print()
// ______________11_____
// / \
// ___3_______ _13_____
// / \ / \
// 1_ _____8____ 12 _15__
// \ / \ / \
// 2 4_ _10 14 16
// \ /
// 5_ 9
// \
// 7
JS
import { RedBlackTree } from 'data-structure-typed';
const rbTree = new RedBlackTree();
// CREATE
rbTree.addMany([11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]);
// READ
rbTree.has(6); // true
const height = rbTree.getHeight(); // 5
// UPDATE
rbTree.delete(10);
rbTree.isAVLBalanced(); // true
// PRINT
rbTree.print();
BST CRUD with Custom Objects
import { BST, BSTNode } from 'data-structure-typed';
const bst = new BST<number, { height: number, age: number }>();
// CREATE
bst.add(11, { "name": "Pablo", "size": 15 });
bst.add(3, { "name": "Kirk", "size": 1 });
bst.addMany(
[15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5],
[
{ "name": "Alice", "size": 15 },
{ "name": "Bob", "size": 1 },
{ "name": "Charlie", "size": 8 },
{ "name": "David", "size": 13 },
{ "name": "Emma", "size": 16 },
{ "name": "Frank", "size": 2 },
{ "name": "Grace", "size": 6 },
{ "name": "Hannah", "size": 9 },
{ "name": "Isaac", "size": 12 },
{ "name": "Jack", "size": 14 },
{ "name": "Katie", "size": 4 },
{ "name": "Liam", "size": 7 },
{ "name": "Mia", "size": 10 },
{ "name": "Noah", "size": 5 }
]
);
// READ
const value = bst.get(11); // { "name": "Pablo", "size": 15 }
const has11 = bst.has(11); // true
// UPDATE
bst.delete(11);
// VERIFY
const afterDelete = bst.has(11); // false
Queue/Deque CRUD
import { Queue, Deque } from 'data-structure-typed';
const queue = new Queue([1, 2, 3, 4, 5]);
// CREATE/ADD
queue.push(6); // O(1)
// READ
const front = queue.peek(); // 1
const size = queue.size; // 6
// REMOVE
const removed = queue.shift(); // O(1) - removes 1
queue.pop(); // O(1) - removes last
// Same API for Deque
const deque = new Deque([1, 2, 3, 4, 5]);
deque.push(6);
deque.unshift(0); // Add to front
deque.pop(); // Remove from end
deque.shift(); // Remove from front
Graph CRUD
import { DirectedGraph } from 'data-structure-typed';
const graph = new DirectedGraph<string>();
// CREATE
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
// CONNECT
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('A', 'C');
// READ
graph.hasVertex('A'); // true
graph.hasEdge('A', 'B'); // true
const neighbors = graph.getNeighbors('A'); // ['B', 'C']
// UPDATE
graph.deleteEdgeSrcToDest('A', 'C');
graph.hasEdge('A', 'C'); // false
// ALGORITHMS
const topologicalOrder = graph.topologicalSort();
const { minDist, minPath } = graph.dijkstra('A', 'C');
π Real-World Examples: Production Code
Example 1: Message Queue - Order Processing
import { Deque } from 'data-structure-typed';
interface Order {
id: string;
customerId: string;
priority: 'normal' | 'urgent';
}
class OrderProcessor {
private normalQueue = new Deque<Order>();
private urgentQueue = new Deque<Order>();
private running = false;
constructor(private readonly urgentBurst: number = 5) {
}
addOrder(order: Order): void {
if (order.priority === 'urgent') {
this.urgentQueue.push(order);
} else {
this.normalQueue.push(order);
}
void this.processOrders();
}
async processOrders(): Promise<void> {
if (this.running) return;
this.running = true;
try {
let urgentStreak = 0;
while (!this.urgentQueue.isEmpty() || !this.normalQueue.isEmpty()) {
const shouldTakeUrgent =
!this.urgentQueue.isEmpty() &&
(urgentStreak < this.urgentBurst || this.normalQueue.isEmpty());
const order = shouldTakeUrgent ? this.urgentQueue.shift() : this.normalQueue.shift();
if (!order) continue;
urgentStreak = order.priority === 'urgent' ? urgentStreak + 1 : 0;
try {
await this.processOrder(order);
} catch (err) {
console.error(`FAILED`, order.id, err);
}
}
} finally {
this.running = false;
}
}
private async processOrder(order: Order): Promise<void> {
await new Promise<void>(r => setTimeout(r, 10));
console.log(
`OK [${order.priority.toUpperCase()}] order:${order.id} customer:${order.customerId}`
);
}
}
Example 2: LRU Cache
class LRUCache<T> {
private cache = new DoublyLinkedList<[string, T]>();
private map = new Map<string, any>();
private maxSize = 100;
get(key: string): T | null {
const node = this.map.get(key);
if (!node) return null;
this.cache.delete(node);
const newNode = this.cache.push(key, node.value[1]);
this.map.set(key, newNode);
return node.value[1];
}
set(key: string, value: T): void {
if (this.map.has(key)) {
const node = this.map.get(key)!;
this.cache.delete(node);
}
const node = this.cache.push(key, value);
this.map.set(key, node);
if (this.cache.length > this.maxSize) {
const oldest = this.cache.shift();
if (oldest) {
this.map.delete(oldest[0]);
}
}
}
}
// Why DoublyLinkedList?
// - O(1) delete from any position
// - O(1) push to end
// - Perfect for LRU implementation
Example 3: Leaderboard System
import { RedBlackTree } from 'data-structure-typed';
interface Player {
id: string;
name: string;
score: number;
}
class Leaderboard {
private scoreTree = new RedBlackTree<number, Player>();
private playerMap = new Map<string, number>();
updateScore(playerId: string, newScore: number): void {
if (this.playerMap.has(playerId)) {
const oldScore = this.playerMap.get(playerId)!;
this.scoreTree.delete(oldScore);
}
const player: Player = { id: playerId, name: playerId, score: newScore };
this.scoreTree.add(newScore, player);
this.playerMap.set(playerId, newScore);
}
getTopN(n: number): Player[] {
return [...this.scoreTree.values()].reverse().slice(0, n) as Player[];
}
getRank(playerId: string): number | null {
const score = this.playerMap.get(playerId);
if (score === undefined) return null;
const higherScores = [...this.scoreTree.keys()].filter(s => s > score).length;
return higherScores + 1;
}
}
// Why RedBlackTree?
// - Automatically maintains sorted order
// - O(log n) insertions and deletions
// - O(log n) searches
// - Perfect for real-time rankings
Example 4: Task Scheduler with Priority Queue
import { MaxPriorityQueue } from 'data-structure-typed';
interface Task {
id: string;
priority: number;
action: () => Promise<void>;
}
class TaskScheduler {
private maxPQ = new MaxPriorityQueue<Task>([], {
comparator: (a, b) => b.priority - a.priority,
});
addTask(task: Task): void {
this.maxPQ.add(task);
}
async start(): Promise<void> {
while (!this.maxPQ.isEmpty()) {
const task = this.maxPQ.poll();
if (!task) break;
try {
await task.action();
console.log(`Task ${task.id} completed (priority: ${task.priority})`);
} catch (error) {
console.error(`Task ${task.id} failed (priority: ${task.priority}):`, error);
}
}
}
}
Example 5: Search Index - Autocomplete
import { Trie } from 'data-structure-typed';
class SearchIndex {
private trie = new Trie();
indexDocument(docId: number, content: string): void {
const words = content.toLowerCase().split(/\s+/);
for (const word of words) {
const existing = this.trie.get(word);
if (!existing) {
this.trie.set(word, [docId]);
} else {
if (!existing.includes(docId)) {
existing.push(docId);
}
this.trie.set(word, existing);
}
}
}
autocomplete(prefix: string): string[] {
return this.trie.getWordsWithPrefix(prefix.toLowerCase());
}
search(word: string): number[] {
return this.trie.get(word.toLowerCase()) ?? [];
}
}
// Why Trie?
// - O(m + k) prefix search (m = prefix length, k = results)
// - Perfect for autocomplete
// - Scales to millions of words
π Migration Guide: From Native JS
Pattern 1: Replacing Array.shift with Deque
β Before:
const queue = [1, 2, 3, 4, 5];
for (let i = 0; i < 100000; i++) {
queue.shift(); // O(n)
}
β After:
import { Deque } from 'data-structure-typed';
const deque = new Deque([1, 2, 3, 4, 5]);
for (let i = 0; i < 100000; i++) {
deque.shift(); // O(1)
}
Pattern 2: Replacing unsorted Map with RedBlackTree
β Before:
const userMap = new Map([
[5, { id: 5, name: 'Alice' }],
[2, { id: 2, name: 'Bob' }],
]);
for (const [id, user] of userMap) {
console.log(id); // 5, 2 (insertion order)
}
β After:
import { RedBlackTree } from 'data-structure-typed';
const userTree = new RedBlackTree([
[5, { id: 5, name: 'Alice' }],
[2, { id: 2, name: 'Bob' }],
]);
for (const [id, user] of userTree) {
console.log(id); // 2, 5 (sorted order)
}
Pattern 3: Replacing Array.sort with PriorityQueue
β Before:
const tasks = [];
for (const task of incomingTasks) {
tasks.push(task);
}
tasks.sort((a, b) => b.priority - a.priority); // O(n log n)
β After:
import { MaxPriorityQueue } from 'data-structure-typed';
const pq = new MaxPriorityQueue();
for (const task of incomingTasks) {
pq.add(task); // O(log n)
}
π― Decision Guide: Choose the Right Data Structure
Need frequent head/tail operations?
β
Yes β Deque (O(1) shift/unshift)
No β Continue
Need sorted + fast queries?
β
Yes β RedBlackTree (O(log n) search)
No β Continue
Need priority handling?
β
Yes β PriorityQueue (O(log n) add)
No β Continue
Need prefix matching?
β
Yes β Trie (O(m + k) search)
No β Continue
Need graph algorithms?
β
Yes β DirectedGraph / UndirectedGraph
No β Use Array
π Data Structures Available
| Data Structure | Unit Test | Perf Test | API Doc | NPM | Downloads |
|---|---|---|---|---|---|
| Binary Tree | β | β | Docs | NPM | |
| Binary Search Tree (BST) | β | β | Docs | NPM | |
| AVL Tree | β | β | Docs | NPM | |
| Red Black Tree | β | β | Docs | NPM | |
| Tree Multimap | β | β | Docs | NPM | |
| Heap | β | β | Docs | NPM | |
| Priority Queue | β | β | Docs | NPM | |
| Max Priority Queue | β | β | Docs | NPM | |
| Min Priority Queue | β | β | Docs | NPM | |
| Trie | β | β | Docs | NPM | |
| Graph | β | β | Docs | NPM | |
| Directed Graph | β | β | Docs | NPM | |
| Undirected Graph | β | β | Docs | NPM | |
| Queue | β | β | Docs | NPM | |
| Deque | β | β | Docs | NPM | |
| Hash Map | β | β | Docs | NPM | |
| Linked List | β | β | Docs | NPM | |
| Singly Linked List | β | β | Docs | NPM | |
| Doubly Linked List | β | β | Docs | NPM | |
| Stack | β | β | Docs | NPM | |
| Segment Tree | β | Docs | NPM | ||
| Binary Indexed Tree | β | Docs | NPM |
π¨ Vivid Examples: Visual Demonstrations
Try it out, or you can run your own code using our visual tool
AVL Tree
Tree Multi Map
Directed Graph
Map Graph
π Integration Examples
React: State Management with Sorted Data
import { useMemo, useState } from 'react';
import { RedBlackTree } from 'data-structure-typed';
type TodoItem = { id: number; title: string };
export default function TodoList() {
const [todoTree, setTodoTree] = useState(
new RedBlackTree<number, TodoItem>([
[100, { id: 100, title: 'Title 100' }],
[1, { id: 1, title: 'Title 1' }],
])
);
const todos = useMemo(() => [...todoTree.values()], [todoTree]);
const addTodo = () => {
setTodoTree((prev) => {
const next = prev.clone();
let id = Math.floor(Math.random() * 100);
while (next.has(id)) {
id = Math.floor(Math.random() * 100);
}
next.add(id, { id, title: `Title ${id}` });
return next;
});
};
const deleteTodo = (id: number) => {
setTodoTree((prev) => {
const next = prev.clone();
next.delete(id);
return next;
});
};
return (
<div>
<h2>Todo List (sorted by id)</h2>
{/* Component implementation */}
</div>
);
}
Express.js: In-Memory Cache with LRU
import express from 'express';
import { DoublyLinkedList } from 'data-structure-typed';
interface CacheEntry {
key: string;
value: any;
}
class ApiCache {
private cache = new DoublyLinkedList<CacheEntry>();
private keyMap = new Map<string, any>();
private maxSize = 1000;
set(key: string, value: any): void {
const entry: CacheEntry = { key, value };
this.cache.push(entry);
this.keyMap.set(key, value);
if (this.cache.size > this.maxSize) {
const oldest = this.cache.shift();
if (oldest) {
this.keyMap.delete(oldest.key);
}
}
}
get(key: string): any {
return this.keyMap.get(key);
}
}
const app = express();
const cache = new ApiCache();
app.get('/api/user/:id', (req, res) => {
const cacheKey = `user:${req.params.id}`;
let userData = cache.get(cacheKey);
if (!userData) {
userData = { id: req.params.id, name: 'User' };
cache.set(cacheKey, userData);
}
res.json(userData);
});
Nest.js: Service with Tree-Based Ranking
import { Injectable } from '@nestjs/common';
import { RedBlackTree } from 'data-structure-typed';
interface RankEntry {
userId: string;
score: number;
}
@Injectable()
export class RankingService {
private rankingTree = new RedBlackTree<number, RankEntry>();
updateScore(userId: string, newScore: number): void {
const existing = [...this.rankingTree.values()].find(
(e) => e?.userId === userId
);
if (existing) {
this.rankingTree.delete(existing.score);
}
this.rankingTree.add(newScore, { userId, score: newScore });
}
getRanking(topN: number = 100): RankEntry[] | undefined {
return [...this.rankingTree.values()].reverse().slice(0, topN);
}
getUserRank(userId: string): number | null {
const allEntries = [...this.rankingTree.values()].reverse();
const index = allEntries.findIndex((e) => e?.userId === userId);
return index >= 0 ? index + 1 : null;
}
}
Free Conversion Between Data Structures
const orgArr = [6, 1, 2, 7, 5, 3, 4, 9, 8];
const entries = [[6, "6"], [1, "1"], [2, "2"], [7, "7"], [5, "5"], [3, "3"], [4, "4"], [9, "9"], [8, "8"]];
const queue = new Queue(orgArr);
queue.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const deque = new Deque(orgArr);
deque.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const sList = new SinglyLinkedList(orgArr);
sList.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const dList = new DoublyLinkedList(orgArr);
dList.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const stack = new Stack(orgArr);
stack.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const minHeap = new MinHeap(orgArr);
minHeap.print();
// [1, 5, 2, 7, 6, 3, 4, 9, 8]
const maxPQ = new MaxPriorityQueue(orgArr);
maxPQ.print();
// [9, 8, 4, 7, 5, 2, 3, 1, 6]
const biTree = new BinaryTree(entries);
biTree.print();
// ___6___
// / \
// ___1_ _2_
// / \ / \
// _7_ 5 3 4
// / \
// 9 8
const bst = new BST(entries);
bst.print();
// _____5___
// / \
// _2_ _7_
// / \ / \
// 1 3_ 6 8_
// \ \
// 4 9
const rbTree = new RedBlackTree(entries);
rbTree.print();
// ___4___
// / \
// _2_ _6___
// / \ / \
// 1 3 5 _8_
// / \
// 7 9
const avl = new AVLTree(entries);
avl.print();
// ___4___
// / \
// _2_ _6___
// / \ / \
// 1 3 5 _8_
// / \
// 7 9
const treeMulti = new TreeMultiMap(entries);
treeMulti.print();
// ___4___
// / \
// _2_ _6___
// / \ / \
// 1 3 5 _8_
// / \
// 7 9
const hm = new HashMap(entries);
hm.print()
// [[6, "6"], [1, "1"], [2, "2"], [7, "7"], [5, "5"], [3, "3"], [4, "4"], [9, "9"], [8, "8"]]
const rbTreeH = new RedBlackTree(hm);
rbTreeH.print();
// ___4___
// / \
// _2_ _6___
// / \ / \
// 1 3 5 _8_
// / \
// 7 9
const pq = new MinPriorityQueue(orgArr);
pq.print();
// [1, 5, 2, 7, 6, 3, 4, 9, 8]
const bst1 = new BST(pq);
bst1.print();
// _____5___
// / \
// _2_ _7_
// / \ / \
// 1 3_ 6 8_
// \ \
// 4 9
const dq1 = new Deque(orgArr);
dq1.print();
// [6, 1, 2, 7, 5, 3, 4, 9, 8]
const rbTree1 = new RedBlackTree(dq1);
rbTree1.print();
// _____5___
// / \
// _2___ _7___
// / \ / \
// 1 _4 6 _9
// / /
// 3 8
Supported Module System
Now you can use it in Node.js and browser environments
Module Formats
- CommonJS:
require export.modules = - ESModule:
import export - TypeScript:
import export - UMD:
var Deque = dataStructureTyped.Deque
CDN
Development
ES Module
<script type="module">
import { BST } from "https://cdn.jsdelivr.net/npm/data-structure-typed/dist/esm/index.mjs";
const bst = new BST([2, 1, 6, 7, 5, 3, 4, 8, 9]);
bst.print();
</sc