UNPKG

ts-data-forge

Version:

[![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.

426 lines 18.5 kB
/** * Interface for an immutable set with O(1) lookup performance and set operation support. * * This interface defines all methods and properties available on ISet instances. All operations * that modify the set return new ISet instances, preserving immutability. The underlying implementation * uses JavaScript's native Set for O(1) average-case performance on add, has, and delete operations. * * **Immutability Guarantees:** * - All mutation operations (add, delete) return new ISet instances * - Original ISet instances are never modified * - Safe for concurrent access and functional programming patterns * * **Performance Characteristics:** * - has/add/delete: O(1) average case * - Set operations (union, intersection, difference): O(n) * - map/filter operations: O(n) * - Iteration: O(n) * * @template K The type of the elements in the set. Must extend MapSetKeyType (string, number, boolean, etc.) */ type ISetInterface<K extends MapSetKeyType> = Readonly<{ /** The number of elements in the set. */ size: SizeType.Arr; /** Checks if the set is empty. */ isEmpty: boolean; /** * Checks if an element exists in the set. * Allows for wider literal types for keys during checking. * @param key The element to check. * @returns `true` if the element exists, `false` otherwise. */ has: (key: K | (WidenLiteral<K> & {})) => boolean; /** * Checks if all elements in the set satisfy a predicate. * @param predicate A function to test each element. * @returns `true` if all elements satisfy the predicate, `false` otherwise. */ every: ((predicate: (key: K) => boolean) => boolean) & /** * Checks if all elements in the set satisfy a type predicate. * Narrows the type of elements in the set if the predicate returns true for all elements. * @template L The narrowed type of the elements. * @param predicate A type predicate function. * @returns `true` if all elements satisfy the predicate, `false` otherwise. */ (<L extends K>(predicate: (key: K) => key is L) => this is ISet<L>); /** * Checks if at least one element in the set satisfies a predicate. * @param predicate A function to test each element. * @returns `true` if at least one element satisfies the predicate, `false` otherwise. */ some: (predicate: (key: K) => boolean) => boolean; /** * Adds an element to the set. * @param key The element to add. * @returns A new ISet instance with the element added. */ add: (key: K) => ISet<K>; /** * Deletes an element from the set. * @param key The element to delete. * @returns A new ISet instance without the specified element. */ delete: (key: K) => ISet<K>; /** * Applies a series of mutations to the set. * @param actions An array of mutation actions (add or delete). * @returns A new ISet instance with all mutations applied. */ withMutations: (actions: readonly Readonly<{ type: 'add'; key: K; } | { type: 'delete'; key: K; }>[]) => ISet<K>; /** * Maps the elements of the set to new elements. * @template K2 The type of the new elements. * @param mapFn A function that maps an element to a new element. * @returns A new ISet instance with mapped elements. */ map: <K2 extends MapSetKeyType>(mapFn: (key: K) => K2) => ISet<K2>; /** * Filters the elements of the set based on a type predicate. * Narrows the type of elements in the resulting set. * @template K2 The narrowed type of the elements. * @param predicate A type predicate function. * @returns A new ISet instance with elements that satisfy the type predicate. */ filter: (<K2 extends K>(predicate: (key: K) => key is K2) => ISet<K2>) & /** * Filters the elements of the set based on a predicate. * @param predicate A function to test each element. * @returns A new ISet instance with elements that satisfy the predicate. */ ((predicate: (key: K) => boolean) => ISet<K>); /** * Filters the elements of the set by excluding elements for which the predicate returns true. * @param predicate A function to test each element. * @returns A new ISet instance with elements for which the predicate returned `false`. */ filterNot: (predicate: (key: K) => boolean) => ISet<K>; /** * Checks if this set is a subset of another set. * @param set The other set. * @returns `true` if this set is a subset of the other set, `false` otherwise. */ isSubsetOf: (set: ISet<WidenLiteral<K>>) => boolean; /** * Checks if this set is a superset of another set. * @param set The other set. * @returns `true` if this set is a superset of the other set, `false` otherwise. */ isSupersetOf: (set: ISet<WidenLiteral<K>>) => boolean; /** * Returns a new set with elements that are in this set but not in another set. * @param set The other set. * @returns A new ISet instance representing the set difference. */ subtract: (set: ISet<K>) => ISet<K>; /** * Returns a new set with elements that are common to both this set and another set. * @param set The other set. * @returns A new ISet instance representing the set intersection. */ intersect: (set: ISet<K>) => ISet<K>; /** * Returns a new set with all elements from both this set and another set. * @template K2 The type of elements in the other set. * @param set The other set. * @returns A new ISet instance representing the set union. */ union: <K2 extends MapSetKeyType>(set: ISet<K2>) => ISet<K | K2>; /** * Executes a callback function for each element in the set. * @param callbackfn A function to execute for each element. */ forEach: (callbackfn: (key: K) => void) => void; /** * Returns an iterator for the elements in the set (alias for values). * @returns An iterable iterator of elements. */ keys: () => IterableIterator<K>; /** * Returns an iterator for the elements in the set. * @returns An iterable iterator of elements. */ values: () => IterableIterator<K>; /** * Returns an iterator for the entries (element-element pairs) in the set. * @returns An iterable iterator of entries. */ entries: () => IterableIterator<readonly [K, K]>; /** * Converts the elements of the set to an array. * @returns A readonly array of elements. */ toArray: () => readonly K[]; /** * Returns the underlying readonly JavaScript Set. * @returns The raw ReadonlySet instance. */ toRawSet: () => ReadonlySet<K>; }>; /** * Represents an immutable set with high-performance operations and comprehensive set algebra support. * * ISet is a persistent data structure that provides all the functionality of JavaScript's Set * while maintaining immutability. All operations that would normally mutate the set instead * return new ISet instances, making it safe for functional programming and concurrent access. * * **Key Features:** * - **Immutable**: All mutation operations return new instances * - **High Performance**: O(1) average-case for has/add/delete operations * - **Set Operations**: Full support for union, intersection, difference, subset/superset checks * - **Type Safe**: Full TypeScript support with generic element types * - **Iterable**: Implements standard JavaScript iteration protocols * - **Functional**: Rich API for map, filter, and functional composition * * **When to Use:** * - Managing collections of unique values with immutability guarantees * - Set algebra operations (unions, intersections, differences) * - Membership testing with O(1) performance * - Functional programming patterns requiring immutable collections * * @template K The type of the elements in the set. Must extend MapSetKeyType. */ export type ISet<K extends MapSetKeyType> = Iterable<K> & ISetInterface<K>; /** * Provides utility functions for ISet. */ export declare namespace ISet { /** * Creates a new ISet instance from an iterable of elements. * * This factory function accepts any iterable of elements, including arrays, * JavaScript Sets, other ISets, or custom iterables. Duplicate elements in the * input iterable will be automatically deduplicated in the resulting set. * * **Performance:** O(n) where n is the number of elements in the iterable. * * @template K The type of the elements. Must extend MapSetKeyType. * @param iterable An iterable of elements (e.g., Array, Set, ISet, etc.) * @returns A new ISet instance containing all unique elements from the iterable. * * @example * ```typescript * // From array (duplicates automatically removed) * const uniqueIds = ISet.create([1, 2, 3, 2, 1]); // Contains: 1, 2, 3 * console.log(uniqueIds.size); // Output: 3 * * // From JavaScript Set * const jsSet = new Set(["red", "green", "blue"]); * const colors = ISet.create(jsSet); * console.log(colors.has("red")); // Output: true * * // From another ISet (creates a copy) * const originalTags = ISet.create(["typescript", "immutable"]); * const copiedTags = ISet.create(originalTags); * console.log(copiedTags.size); // Output: 2 * * // Empty set * const emptyPermissions = ISet.create<string>([]); * console.log(emptyPermissions.isEmpty); // Output: true * * // Fluent operations * const processedNumbers = ISet.create([1, 2, 3, 4, 5]) * .filter(x => x % 2 === 0) // Keep even numbers: 2, 4 * .add(6) // Add 6: 2, 4, 6 * .delete(2); // Remove 2: 4, 6 * console.log(processedNumbers.toArray().toSorted()); // Output: [4, 6] * * // From generator function * function* generatePrimes(): Generator<number> { * yield 2; yield 3; yield 5; yield 7; * } * const primes = ISet.create(generatePrimes()); * console.log(primes.size); // Output: 4 * ``` */ const create: <K extends MapSetKeyType>(iterable: Iterable<K>) => ISet<K>; /** * Checks if two ISet instances are structurally equal. * * Two ISets are considered equal if they have the same size and contain exactly the same * elements. The order of elements does not matter for equality comparison since sets are * unordered collections. Elements are compared using JavaScript's `===` operator. * * **Performance:** O(n) where n is the size of the smaller set. * * @template K The type of the elements. * @param a The first ISet instance to compare. * @param b The second ISet instance to compare. * @returns `true` if the sets contain exactly the same elements, `false` otherwise. * * @example * ```typescript * // Basic equality comparison * const permissions1 = ISet.create(["read", "write", "execute"]); * const permissions2 = ISet.create(["execute", "read", "write"]); // Order doesn't matter * const permissions3 = ISet.create(["read", "write"]); * * console.log(ISet.equal(permissions1, permissions2)); // true * console.log(ISet.equal(permissions1, permissions3)); // false (different sizes) * * // With different element types * const numbers1 = ISet.create([1, 2, 3]); * const numbers2 = ISet.create([3, 1, 2]); * const numbers3 = ISet.create([1, 2, 4]); // Different element * * console.log(ISet.equal(numbers1, numbers2)); // true * console.log(ISet.equal(numbers1, numbers3)); // false * * // Empty sets * const empty1 = ISet.create<string>([]); * const empty2 = ISet.create<string>([]); * console.log(ISet.equal(empty1, empty2)); // true * * // Single element sets * const single1 = ISet.create(["unique"]); * const single2 = ISet.create(["unique"]); * const single3 = ISet.create(["different"]); * * console.log(ISet.equal(single1, single2)); // true * console.log(ISet.equal(single1, single3)); // false * ``` */ const equal: <K extends MapSetKeyType>(a: ISet<K>, b: ISet<K>) => boolean; /** * Computes the difference between two ISet instances, identifying added and deleted elements. * * This function performs a set difference operation to determine what elements were added * and what elements were deleted when transitioning from the old set to the new set. * This is useful for change detection, state management, and synchronization scenarios. * * **Performance:** O(n + m) where n and m are the sizes of the old and new sets respectively. * * @template K The type of the elements. * @param oldSet The original set representing the previous state. * @param newSet The new set representing the current state. * @returns An object with `added` and `deleted` properties, each containing an ISet * of elements that were added or removed respectively. * * @example * ```typescript * // User permission changes * const oldPermissions = ISet.create(["read", "write", "delete"]); * const newPermissions = ISet.create(["read", "write", "execute", "admin"]); * * const permissionDiff = ISet.diff(oldPermissions, newPermissions); * * console.log("Permissions removed:", permissionDiff.deleted.toArray()); * // Output: ["delete"] * * console.log("Permissions added:", permissionDiff.added.toArray()); * // Output: ["execute", "admin"] * * // No changes * const unchanged1 = ISet.create(["a", "b", "c"]); * const unchanged2 = ISet.create(["a", "b", "c"]); * const noDiff = ISet.diff(unchanged1, unchanged2); * * console.log(noDiff.added.isEmpty); // true * console.log(noDiff.deleted.isEmpty); // true * * // Complete replacement * const oldTags = ISet.create(["javascript", "react"]); * const newTags = ISet.create(["typescript", "vue"]); * const tagDiff = ISet.diff(oldTags, newTags); * * console.log(tagDiff.deleted.toArray()); // ["javascript", "react"] * console.log(tagDiff.added.toArray()); // ["typescript", "vue"] * ``` */ const diff: <K extends MapSetKeyType>(oldSet: ISet<K>, newSet: ISet<K>) => ReadonlyRecord<"added" | "deleted", ISet<K>>; /** * Computes the intersection of two ISet instances. * * Returns a new set containing only the elements that are present in both input sets. * This operation is commutative: `intersection(a, b) === intersection(b, a)`. * * **Performance:** O(min(n, m)) where n and m are the sizes of the input sets. * * @template K The type of the elements. * @param a The first set. * @param b The second set. * @returns A new ISet instance containing elements common to both sets. * * @example * ```typescript * // Finding common permissions between user and role * const userPermissions = ISet.create(["read", "write", "delete", "admin"]); * const rolePermissions = ISet.create(["read", "write", "execute"]); * * const commonPermissions = ISet.intersection(userPermissions, rolePermissions); * console.log(commonPermissions.toArray()); // ["read", "write"] * * // No common elements * const setA = ISet.create([1, 2, 3]); * const setB = ISet.create([4, 5, 6]); * const noCommon = ISet.intersection(setA, setB); * console.log(noCommon.isEmpty); // true * * // Complete overlap * const identical1 = ISet.create(["a", "b", "c"]); * const identical2 = ISet.create(["a", "b", "c"]); * const completeOverlap = ISet.intersection(identical1, identical2); * console.log(ISet.equal(completeOverlap, identical1)); // true * * // Intersection with empty set * const nonEmpty = ISet.create([1, 2, 3]); * const empty = ISet.create<number>([]); * const withEmpty = ISet.intersection(nonEmpty, empty); * console.log(withEmpty.isEmpty); // true * ``` */ const intersection: <K extends MapSetKeyType>(a: ISet<K>, b: ISet<K>) => ISet<K>; /** * Computes the union of two ISet instances. * * Returns a new set containing all elements that are present in either input set. * Duplicate elements are automatically handled since sets only contain unique values. * This operation is commutative: `union(a, b) === union(b, a)`. * * **Performance:** O(n + m) where n and m are the sizes of the input sets. * * @template K1 The type of elements in the first set. * @template K2 The type of elements in the second set. * @param a The first set. * @param b The second set. * @returns A new ISet instance containing all elements from both sets. * * @example * ```typescript * // Combining permissions from multiple sources * const userPermissions = ISet.create(["read", "write"]); * const rolePermissions = ISet.create(["write", "execute", "admin"]); * * const allPermissions = ISet.union(userPermissions, rolePermissions); * console.log(allPermissions.toArray().toSorted()); * // Output: ["admin", "execute", "read", "write"] * * // Union with different types (type widening) * const numbers = ISet.create([1, 2, 3]); * const strings = ISet.create(["a", "b"]); * const mixed = ISet.union(numbers, strings); // ISet<number | string> * console.log(mixed.size); // 5 * * // Union with empty set * const nonEmpty = ISet.create(["item1", "item2"]); * const empty = ISet.create<string>([]); * const withEmpty = ISet.union(nonEmpty, empty); * console.log(ISet.equal(withEmpty, nonEmpty)); // true * * // Overlapping sets * const featuresA = ISet.create(["feature1", "feature2", "feature3"]); * const featuresB = ISet.create(["feature2", "feature3", "feature4"]); * const allFeatures = ISet.union(featuresA, featuresB); * console.log(allFeatures.size); // 4 (duplicates removed) * ``` */ const union: <K1 extends MapSetKeyType, K2 extends MapSetKeyType>(a: ISet<K1>, b: ISet<K2>) => ISet<K1 | K2>; } export {}; //# sourceMappingURL=iset.d.mts.map