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.

437 lines (434 loc) 16.7 kB
import '../number/branded-types/finite-number.mjs'; import '../number/branded-types/int.mjs'; import '../number/branded-types/int16.mjs'; import '../number/branded-types/int32.mjs'; import '../number/branded-types/non-negative-finite-number.mjs'; import '../number/branded-types/non-negative-int16.mjs'; import '../number/branded-types/non-negative-int32.mjs'; import '../number/branded-types/non-zero-finite-number.mjs'; import '../number/branded-types/non-zero-int.mjs'; import '../number/branded-types/non-zero-int16.mjs'; import '../number/branded-types/non-zero-int32.mjs'; import '../number/branded-types/non-zero-safe-int.mjs'; import '../number/branded-types/non-zero-uint16.mjs'; import '../number/branded-types/non-zero-uint32.mjs'; import '../number/branded-types/positive-finite-number.mjs'; import '../number/branded-types/positive-int.mjs'; import '../number/branded-types/positive-int16.mjs'; import '../number/branded-types/positive-int32.mjs'; import '../number/branded-types/positive-safe-int.mjs'; import '../number/branded-types/positive-uint16.mjs'; import '../number/branded-types/positive-uint32.mjs'; import '../number/branded-types/safe-int.mjs'; import '../number/branded-types/safe-uint.mjs'; import '../number/branded-types/uint.mjs'; import '../number/branded-types/uint16.mjs'; import { asUint32 } from '../number/branded-types/uint32.mjs'; import '../number/enum/int8.mjs'; import '../number/enum/uint8.mjs'; import '../number/num.mjs'; import '../number/refined-number-utils.mjs'; import { unknownToString } from '../others/unknown-to-string.mjs'; // No imports from functional needed anymore /** * Provides utility functions for ISet. */ var ISet; (function (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 * ``` */ ISet.create = (iterable) => new ISetClass(iterable); /** * 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 * ``` */ ISet.equal = (a, b) => a.size === b.size && a.every((e) => b.has(e)); /** * 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"] * ``` */ ISet.diff = (oldSet, newSet) => ({ deleted: oldSet.subtract(newSet), added: newSet.subtract(oldSet), }); /** * 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 * ``` */ ISet.intersection = (a, b) => a.intersect(b); /** * 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) * ``` */ ISet.union = (a, b) => a.union(b); })(ISet || (ISet = {})); /** * Internal class implementation for ISet providing immutable set operations. * * This class implements the ISet interface using JavaScript's native Set as the underlying * storage mechanism for optimal performance. All mutation operations create new instances * rather than modifying the existing set, ensuring immutability. * * **Implementation Details:** * - Uses ReadonlySet<K> internally for type safety and performance * - Implements copy-on-write semantics for efficiency * - Provides optional debug messaging for development * * @template K The type of the elements. Must extend MapSetKeyType. * @implements ISet * @implements Iterable * @internal This class should not be used directly. Use ISet.create() instead. */ class ISetClass { #set; #showNotFoundMessage; /** * Constructs an ISetClass instance with the given elements. * * @param iterable An iterable of elements to populate the set. * @param showNotFoundMessage Whether to log warning messages when operations * are performed on non-existent elements. Useful for debugging. * Defaults to false for production use. * @internal Use ISet.create() instead of calling this constructor directly. */ constructor(iterable, showNotFoundMessage = false) { this.#set = new Set(iterable); this.#showNotFoundMessage = showNotFoundMessage; } /** @inheritdoc */ get size() { return asUint32(this.#set.size); } /** @inheritdoc */ get isEmpty() { return this.size === 0; } /** @inheritdoc */ has(key) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return this.#set.has(key); } /** @inheritdoc */ every(predicate) { for (const key of this.values()) { if (!predicate(key)) return false; } return true; } /** @inheritdoc */ some(predicate) { for (const key of this.values()) { if (predicate(key)) return true; } return false; } /** @inheritdoc */ add(key) { if (this.has(key)) return this; return ISet.create([...this.#set, key]); } /** @inheritdoc */ delete(key) { if (!this.has(key)) { if (this.#showNotFoundMessage) { const keyStr = unknownToString(key); console.warn(`ISet.delete: key not found: ${keyStr}`); } return this; } return ISet.create(Array.from(this.#set).filter((k) => !Object.is(k, key))); } /** @inheritdoc */ withMutations(actions) { const mut_result = new Set(this.#set); for (const action of actions) { switch (action.type) { case 'delete': mut_result.delete(action.key); break; case 'add': mut_result.add(action.key); break; } } return ISet.create(mut_result); } /** @inheritdoc */ map(mapFn) { return ISet.create(this.toArray().map(mapFn)); } /** @inheritdoc */ filter(predicate) { return ISet.create(this.toArray().filter(predicate)); } /** @inheritdoc */ filterNot(predicate) { return ISet.create(this.toArray().filter((e) => !predicate(e))); } /** @inheritdoc */ forEach(callbackfn) { for (const v of this.#set.values()) { callbackfn(v); } } /** @inheritdoc */ isSubsetOf(set) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return this.every((k) => set.has(k)); } /** @inheritdoc */ isSupersetOf(set) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return set.every((k) => this.has(k)); } /** @inheritdoc */ subtract(set) { return ISet.create(this.toArray().filter((k) => !set.has(k))); } /** @inheritdoc */ intersect(set) { return ISet.create(this.toArray().filter((k) => set.has(k))); } /** @inheritdoc */ union(set) { return ISet.create([...this, ...set]); } /** * @inheritdoc */ [Symbol.iterator]() { return this.#set[Symbol.iterator](); } /** @inheritdoc */ keys() { return this.#set.keys(); } /** @inheritdoc */ values() { return this.#set.values(); } /** @inheritdoc */ entries() { return this.#set.entries(); } /** @inheritdoc */ toArray() { return Array.from(this.values()); } /** @inheritdoc */ toRawSet() { return this.#set; } } export { ISet }; //# sourceMappingURL=iset.mjs.map