UNPKG

@mirei/ts-collections

Version:

A collection of wrappers for common data structures in TypeScript

651 lines (509 loc) 21.4 kB
# ts-collections A TypeScript library providing a comprehensive set of data structures with a focus on type safety and performance. ## Table of Contents - [Installation](#installation) - [Data Structures](#data-structures) - [List](#list) - [List](#list-1) - [LinkedList](#linkedlist) - [CircularLinkedList](#circularlinkedlist) - [Dictionary](#dictionary) - [Dictionary](#dictionary-1) - [SortedDictionary](#sorteddictionary) - [Set](#set) - [EnumerableSet](#enumerableset) - [SortedSet](#sortedset) - [Queue](#queue) - [Queue](#queue-1) - [CircularQueue](#circularqueue) - [PriorityQueue](#priorityqueue) - [Stack](#stack) - [Stack](#stack-1) - [Heap](#heap) - [Heap](#heap-1) - [Tree](#tree) - [RedBlackTree](#redblacktree) - [Lookup](#lookup) - [Lookup](#lookup-1) - [Observable Collections](#observable-collections) - [ObservableCollection](#observablecollection) - [ReadonlyObservableCollection](#readonlyobservablecollection) - [Readonly Collections](#readonly-collections) - [ReadonlyCollection](#readonlycollection) - [ReadonlyDictionary](#readonlydictionary) - [ReadonlyList](#readonlylist) - [Immutable Data Structures](#immutable-data-structures) - [ImmutableList](#immutablelist) - [ImmutableDictionary](#immutabledictionary) - [ImmutableSortedDictionary](#immutablesorteddictionary) - [ImmutableSet](#immutableset) - [ImmutableSortedSet](#immutablesortedset) - [ImmutableQueue](#immutablequeue) - [ImmutablePriorityQueue](#immutablepriorityqueue) - [ImmutableStack](#immutablestack) - [Enumerable Support](#enumerable-support) ## Installation ```shell npm i @mirei/ts-collections ``` ## Data Structures ### List Lists are ordered collections that allow duplicate elements and provide index-based access. #### List A dynamic array-based implementation of a list. - **When to use**: When you need fast random access by index and efficient operations at the end of the collection. - **Key features**: - Fast random access by index (O(1)) - Efficient add/remove at the end (O(1) amortized) - Less efficient add/remove at arbitrary positions (O(n)) - **Example usage**: ```typescript const list = new List([1, 2, 3, 4, 5]); list.add(6); // Add element at the end list.addAt(0, 0); // Add element at specific index list.get(3); // Get element at index 3 list.removeAt(0); // Remove element at index 0 list.sort(); // Sort the list ``` #### LinkedList A doubly-linked list implementation. - **When to use**: When you need efficient insertions and deletions at both ends or in the middle of the list. - **Key features**: - O(1) operations at both ends (add, remove) - O(1) insertion/deletion after finding a position - O(n) random access by index - Supports queue and stack operations (addFirst, addLast, removeFirst, removeLast) - **Example usage**: ```typescript const linkedList = new LinkedList([1, 2, 3]); linkedList.addFirst(0); // Add at beginning linkedList.addLast(4); // Add at end linkedList.removeFirst(); // Remove from beginning linkedList.removeLast(); // Remove from end ``` #### CircularLinkedList A circular doubly-linked list implementation where the last node points to the first node and the first node points to the last node, forming a circle. - **When to use**: When you need a linked list with circular traversal capabilities or when you need to efficiently access both ends of the list. - **Key features**: - O(1) operations at both ends (addFirst, addLast, removeFirst, removeLast) - Circular structure allows wrapping around from the end to the beginning - Supports efficient traversal in both directions - Optimized node access by traversing from the closer end - **Example usage**: ```typescript const circularList = new CircularLinkedList([1, 2, 3]); circularList.addFirst(0); // Add at beginning circularList.addLast(4); // Add at end circularList.removeFirst(); // Remove from beginning circularList.removeLast(); // Remove from end // Get a range of elements that can wrap around the list const range = circularList.getRange(2, 4); ``` ### Dictionary Dictionaries are collections of key-value pairs where each key is unique. #### Dictionary A hash-based implementation of a dictionary using JavaScript's Map. - **When to use**: When you need fast lookups by key and don't need the keys to be ordered. - **Key features**: - Fast lookups, insertions, and deletions (O(1) average) - Keys are not ordered - Supports custom equality comparators - **Example usage**: ```typescript const dict = new Dictionary<string, number>(); dict.add("one", 1); // Add a key-value pair dict.put("two", 2); // Add or update a key-value pair dict.get("one"); // Get value by key dict.remove("one"); // Remove a key-value pair dict.containsKey("two"); // Check if key exists ``` #### SortedDictionary A dictionary implementation that keeps keys sorted using a Red-Black Tree. - **When to use**: When you need a dictionary with keys maintained in sorted order. - **Key features**: - Guaranteed O(log n) lookups, insertions, and deletions - Keys are always sorted - Supports custom key comparators - **Example usage**: ```typescript const sortedDict = new SortedDictionary<number, string>(); sortedDict.add(3, "three"); sortedDict.add(1, "one"); sortedDict.add(2, "two"); // Iteration will be in order: 1, 2, 3 for (const pair of sortedDict) { console.log(pair.key, pair.value); } ``` ### Set Sets are collections of unique elements. #### EnumerableSet A set implementation based on JavaScript's Set. - **When to use**: When you need a collection of unique elements with fast lookups. - **Key features**: - Fast lookups, insertions, and deletions (O(1) average) - Elements are not ordered - Supports set operations (union, intersection, difference) - **Example usage**: ```typescript const set = new EnumerableSet([1, 2, 3]); set.add(4); // Add an element set.contains(2); // Check if element exists set.remove(1); // Remove an element set.intersectWith([2, 3, 5]); // Keep only elements that are in both sets ``` #### SortedSet A set implementation that keeps elements sorted using a Red-Black Tree. - **When to use**: When you need a set with elements maintained in sorted order. - **Key features**: - Guaranteed O(log n) lookups, insertions, and deletions - Elements are always sorted - Supports custom element comparators - Supports range operations (headSet, tailSet, subSet) - **Example usage**: ```typescript const sortedSet = new SortedSet([3, 1, 4, 2]); // Iteration will be in order: 1, 2, 3, 4 for (const element of sortedSet) { console.log(element); } // Get subsets const headSet = sortedSet.headSet(3); // Elements less than 3 const tailSet = sortedSet.tailSet(2); // Elements greater than or equal to 2 ``` ### Queue Queues are FIFO (First-In-First-Out) collections. #### Queue A standard queue implementation using a LinkedList. - **When to use**: When you need a FIFO data structure. - **Key features**: - O(1) operations at both ends (enqueue, dequeue) - Supports peeking at the front element without removing it - **Example usage**: ```typescript const queue = new Queue([1, 2, 3]); queue.enqueue(4); // Add to the end const front = queue.peek(); // Look at the front element const item = queue.dequeue(); // Remove and return the front element ``` #### CircularQueue A fixed-size queue that overwrites the oldest elements when full. - **When to use**: When you need a queue with a fixed capacity that automatically removes old elements. - **Key features**: - Fixed capacity (default 32) - Automatically removes oldest elements when full - O(1) operations at both ends - **Example usage**: ```typescript const circularQueue = new CircularQueue<number>(5); // Capacity of 5 for (let i = 0; i < 10; i++) { circularQueue.enqueue(i); } // Queue will contain only the 5 most recent elements: 5, 6, 7, 8, 9 ``` #### PriorityQueue A queue where elements are dequeued according to priority. - **When to use**: When you need to process elements in order of priority rather than insertion order. - **Key features**: - O(log n) insertion and removal - Highest priority element is always at the front - Supports custom priority comparators - **Example usage**: ```typescript // Min priority queue (smallest element first) const priorityQueue = new PriorityQueue<number>([3, 1, 4, 2]); priorityQueue.enqueue(5); // Elements will be dequeued in order: 1, 2, 3, 4, 5 ``` ### Stack Stacks are LIFO (Last-In-First-Out) collections. #### Stack A standard stack implementation using a LinkedList. - **When to use**: When you need a LIFO data structure. - **Key features**: - O(1) operations at the top (push, pop) - Supports peeking at the top element without removing it - **Example usage**: ```typescript const stack = new Stack([1, 2, 3]); stack.push(4); // Add to the top const top = stack.peek(); // Look at the top element const item = stack.pop(); // Remove and return the top element ``` ### Heap A binary heap is a complete binary tree where each node's value is greater than or equal to (max heap) or less than or equal to (min heap) the values of its children. #### Heap A binary heap implementation. - **When to use**: When you need to efficiently find and remove the minimum or maximum element. - **Key features**: - O(1) access to the minimum/maximum element - O(log n) insertion and removal - Supports custom comparators to create min or max heaps - **Example usage**: ```typescript // Min heap (smallest element at the root) const minHeap = new Heap<number>((a, b) => a - b); minHeap.add(3); minHeap.add(1); minHeap.add(4); const min = minHeap.peek(); // Get the minimum element (1) minHeap.poll(); // Remove and return the minimum element ``` ### Tree Trees are hierarchical data structures. #### RedBlackTree A self-balancing binary search tree implementation. - **When to use**: When you need a balanced tree for efficient lookups, insertions, and deletions. - **Key features**: - Guaranteed O(log n) lookups, insertions, and deletions - Elements are always sorted - Supports custom element comparators - **Example usage**: ```typescript const tree = new RedBlackTree<number>(); tree.insert(3); tree.insert(1); tree.insert(4); tree.search(1); // Check if element exists tree.delete(3); // Remove an element ``` ### Lookup A lookup is a collection that maps keys to collections of values. #### Lookup A lookup implementation using a RedBlackTree. - **When to use**: When you need to group elements by a key and access all elements with a specific key. - **Key features**: - O(log n) lookups by key - Each key maps to a collection of values - Supports custom key comparators - **Example usage**: ```typescript const data = [ { category: "A", value: 1 }, { category: "B", value: 2 }, { category: "A", value: 3 } ]; const lookup = Lookup.create( data, item => item.category, item => item.value ); const categoryA = lookup.get("A"); // Returns collection with values 1 and 3 ``` ### Observable Collections Observable collections are collections that notify subscribers when changes occur. #### ObservableCollection A collection that notifies subscribers when elements are added, removed, or modified. - **When to use**: When you need to track changes to a collection and react to those changes. - **Key features**: - Notifies subscribers when elements are added, removed, or modified - Provides information about what changed (old items, new items, action type) - Wraps a List for efficient operations - **Example usage**: ```typescript const collection = new ObservableCollection([1, 2, 3]); collection.collectionChanged = (sender, args) => { console.log("Collection changed:", args.action); console.log("New items:", args.newItems); console.log("Old items:", args.oldItems); }; collection.add(4); // Triggers collectionChanged event collection.remove(2); // Triggers collectionChanged event collection.clear(); // Triggers collectionChanged event ``` #### ReadonlyObservableCollection A read-only wrapper around an ObservableCollection that forwards collection change events. - **When to use**: When you need to provide read-only access to an observable collection while still allowing subscribers to be notified of changes. - **Key features**: - Provides read-only access to the underlying collection - Forwards collection change events from the wrapped collection - Prevents modification of the collection through this wrapper - **Example usage**: ```typescript const observableCollection = new ObservableCollection([1, 2, 3]); const readonlyCollection = new ReadonlyObservableCollection(observableCollection); readonlyCollection.collectionChanged = (sender, args) => { console.log("Collection changed:", args.action); }; // Changes to the original collection are still observable through the readonly wrapper observableCollection.add(4); // Triggers collectionChanged event on readonlyCollection ``` ### Readonly Collections Readonly collections are wrappers that provide read-only access to underlying collections. #### ReadonlyCollection A wrapper around an ICollection that provides read-only access to the underlying collection. - **When to use**: When you need to provide read-only access to a collection to prevent modifications. - **Key features**: - Provides read-only access to the underlying collection - Delegates all operations to the wrapped collection - Prevents modification of the collection through this wrapper - **Example usage**: ```typescript const list = new List([1, 2, 3]); const readonlyCollection = new ReadonlyCollection(list); readonlyCollection.contains(2); // Returns true // readonlyCollection.add(4); // Error: Method not available // Changes to the original collection are reflected in the readonly wrapper list.add(4); readonlyCollection.contains(4); // Returns true ``` #### ReadonlyDictionary A wrapper around an IDictionary that provides read-only access to the underlying dictionary. - **When to use**: When you need to provide read-only access to a dictionary to prevent modifications. - **Key features**: - Provides read-only access to the underlying dictionary - Delegates all operations to the wrapped dictionary - Prevents modification of the dictionary through this wrapper - **Example usage**: ```typescript const dict = new Dictionary<string, number>(); dict.add("one", 1); dict.add("two", 2); const readonlyDict = new ReadonlyDictionary(dict); readonlyDict.get("one"); // Returns 1 readonlyDict.containsKey("two"); // Returns true // readonlyDict.add("three", 3); // Error: Method not available // Changes to the original dictionary are reflected in the readonly wrapper dict.add("three", 3); readonlyDict.containsKey("three"); // Returns true ``` #### ReadonlyList A wrapper around an IList that provides read-only access to the underlying list. - **When to use**: When you need to provide read-only access to a list to prevent modifications. - **Key features**: - Provides read-only access to the underlying list - Delegates all operations to the wrapped list - Supports index-based access - Prevents modification of the list through this wrapper - **Example usage**: ```typescript const list = new List([1, 2, 3]); const readonlyList = new ReadonlyList(list); readonlyList.get(0); // Returns 1 readonlyList.indexOf(2); // Returns 1 // readonlyList.add(4); // Error: Method not available // Changes to the original list are reflected in the readonly wrapper list.add(4); readonlyList.contains(4); // Returns true ``` ### Immutable Data Structures Immutable data structures are collections that cannot be modified after they are created. Any operation that would modify the structure instead returns a new instance with the modification applied, leaving the original structure unchanged. #### ImmutableList An immutable version of List. - **When to use**: When you need a list that cannot be modified, or when you want to ensure that a list passed to a function is not modified. - **Key features**: - All operations that would modify the list return a new list - Supports all standard list operations - **Example usage**: ```typescript const list = ImmutableList.create([1, 2, 3]); const newList = list.add(4); // Original list is unchanged const filtered = list.removeIf(x => x % 2 === 0); // Returns new list with odd numbers only ``` #### ImmutableDictionary An immutable version of Dictionary. - **When to use**: When you need a dictionary that cannot be modified, or when you want to ensure that a dictionary passed to a function is not modified. - **Key features**: - All operations that would modify the dictionary return a new dictionary - Supports all standard dictionary operations - **Example usage**: ```typescript const dict = ImmutableDictionary.create<string, number>(); const dict2 = dict.add("one", 1); // Original dictionary is unchanged const dict3 = dict2.put("two", 2); // Returns new dictionary with the added key-value pair ``` #### ImmutableSortedDictionary An immutable version of SortedDictionary. - **When to use**: When you need a sorted dictionary that cannot be modified. - **Key features**: - All operations that would modify the dictionary return a new dictionary - Keys are always sorted - **Example usage**: ```typescript const sortedDict = ImmutableSortedDictionary.create<number, string>(); const dict2 = sortedDict.add(3, "three"); const dict3 = dict2.add(1, "one"); // Iteration will be in order: 1, 3 ``` #### ImmutableSet An immutable version of EnumerableSet. - **When to use**: When you need a set that cannot be modified. - **Key features**: - All operations that would modify the set return a new set - Supports all standard set operations - **Example usage**: ```typescript const set = ImmutableSet.create([1, 2, 3]); const set2 = set.add(4); // Original set is unchanged const set3 = set2.remove(1); // Returns new set without the element 1 ``` #### ImmutableSortedSet An immutable version of SortedSet. - **When to use**: When you need a sorted set that cannot be modified. - **Key features**: - All operations that would modify the set return a new set - Elements are always sorted - **Example usage**: ```typescript const sortedSet = ImmutableSortedSet.create([3, 1, 4, 2]); const set2 = sortedSet.add(5); // Original set is unchanged // Iteration will be in order: 1, 2, 3, 4, 5 ``` #### ImmutableQueue An immutable version of Queue. - **When to use**: When you need a queue that cannot be modified. - **Key features**: - All operations that would modify the queue return a new queue - Supports all standard queue operations - **Example usage**: ```typescript const queue = ImmutableQueue.create([1, 2, 3]); const queue2 = queue.add(4); // Original queue is unchanged // Elements will be dequeued in order: 1, 2, 3, 4 ``` #### ImmutableStack An immutable version of Stack. - **When to use**: When you need a stack that cannot be modified. - **Key features**: - All operations that would modify the stack return a new stack - Supports all standard stack operations - **Example usage**: ```typescript const stack = ImmutableStack.create([1, 2, 3]); const stack2 = stack.add(4); // Original stack is unchanged // Elements will be popped in order: 4, 3, 2, 1 ``` #### ImmutablePriorityQueue An immutable version of PriorityQueue. - **When to use**: When you need a priority queue that cannot be modified, or when you want to ensure that elements are processed in priority order without modifying the original collection. - **Key features**: - All operations that would modify the queue return a new queue - Elements are dequeued according to priority - Supports custom priority comparators - Provides both queue operations (enqueue/dequeue) and collection operations (add/remove) - **Example usage**: ```typescript // Min priority queue (smallest element first) const queue = ImmutablePriorityQueue.create([3, 1, 4, 2]); const queue2 = queue.enqueue(5); // Original queue is unchanged const front = queue2.peek(); // Returns 1 (smallest element) const queue3 = queue2.dequeue(); // Returns new queue without the highest priority element ``` ## Enumerable Support All collections in this library implement the `IEnumerable` interface, providing LINQ-like operations for querying and manipulating data. ### Example Usage ```typescript const list = new List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); const list2 = list.select(n => n * n) .takeWhile(n => n <= 25) .skipWhile(n => n < 10) .orderByDescending(n => n) .toList(); const array = list.takeLast(5).toArray(); ``` You can also use Enumerable with plain arrays. ```typescript const array = Enumerable.from([1, 2, 3, 4, 5]) .where(n => n % 2 !== 0) .toArray(); ```