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.

339 lines (336 loc) 12.3 kB
import { Optional } from '../functional/optional.mjs'; import '../functional/result.mjs'; 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 { tp } from '../others/tuple.mjs'; import { unknownToString } from '../others/unknown-to-string.mjs'; /** * Provides utility functions for IMap. */ var IMap; (function (IMap) { /** * Creates a new IMap instance from an iterable of key-value pairs. * * This factory function accepts any iterable of [key, value] tuples, including arrays, * JavaScript Maps, other IMaps, or custom iterables. The resulting IMap will contain * all the entries from the input iterable. * * **Performance:** O(n) where n is the number of entries in the iterable. * * @template K The type of the keys. Must extend MapSetKeyType. * @template V The type of the values. * @param iterable An iterable of key-value pairs (e.g., Array, Map, IMap, etc.) * @returns A new IMap instance containing all entries from the iterable. * * @example * ```typescript * // From array of tuples * const userScores = IMap.create<string, number>([ * ["alice", 95], * ["bob", 87], * ["charlie", 92] * ]); * console.log(userScores.get("alice").unwrap()); // Output: 95 * * // From JavaScript Map * const jsMap = new Map([["config", { debug: true }], ["env", "production"]]); * const config = IMap.create(jsMap); * console.log(config.get("env").unwrap()); // Output: "production" * * // From another IMap (creates a copy) * const originalMap = IMap.create<string, boolean>([["enabled", true]]); * const copiedMap = IMap.create(originalMap); * console.log(copiedMap.get("enabled").unwrap()); // Output: true * * // Empty map * const emptyMap = IMap.create<string, number>([]); * console.log(emptyMap.size); // Output: 0 * * // From custom iterable * function* generateEntries(): Generator<[string, number]> { * for (const i of range(3)) { * yield [`item${i}`, i * 10]; * } * } * const generatedMap = IMap.create(generateEntries()); * console.log(generatedMap.size); // Output: 3 * ``` */ IMap.create = (iterable) => new IMapClass(iterable); /** * Checks if two IMap instances are structurally equal. * * Two IMaps are considered equal if they have the same size and contain exactly the same * key-value pairs. The order of entries does not matter for equality comparison. * Values are compared using JavaScript's `===` operator. * * **Performance:** O(n) where n is the size of the smaller map. * * @template K The type of the keys. * @template V The type of the values. * @param a The first IMap instance to compare. * @param b The second IMap instance to compare. * @returns `true` if the maps contain exactly the same key-value pairs, `false` otherwise. * * @example * ```typescript * // Basic equality comparison * const preferences1 = IMap.create<string, boolean>([ * ["darkMode", true], * ["notifications", false] * ]); * const preferences2 = IMap.create<string, boolean>([ * ["darkMode", true], * ["notifications", false] * ]); * const preferences3 = IMap.create<string, boolean>([ * ["notifications", false], * ["darkMode", true] // Order doesn't matter * ]); * * console.log(IMap.equal(preferences1, preferences2)); // true * console.log(IMap.equal(preferences1, preferences3)); // true (order doesn't matter) * * // Different values * const preferences4 = IMap.create<string, boolean>([ * ["darkMode", false], // Different value * ["notifications", false] * ]); * console.log(IMap.equal(preferences1, preferences4)); // false * * // Different keys * const preferences5 = IMap.create<string, boolean>([ * ["darkMode", true], * ["sounds", false] // Different key * ]); * console.log(IMap.equal(preferences1, preferences5)); // false * * // Empty maps * const empty1 = IMap.create<string, number>([]); * const empty2 = IMap.create<string, number>([]); * console.log(IMap.equal(empty1, empty2)); // true * * // Note: For deep equality of object values, use a custom comparison * const users1 = IMap.create<string, User>([["1", { name: "Alice" }]]); * const users2 = IMap.create<string, User>([["1", { name: "Alice" }]]); * console.log(IMap.equal(users1, users2)); // false (different object references) * ``` */ IMap.equal = (a, b) => a.size === b.size && a.every((v, k) => b.get(k) === v); })(IMap || (IMap = {})); /** * Internal class implementation for IMap providing immutable map operations. * * This class implements the IMap interface using JavaScript's native Map as the underlying * storage mechanism for optimal performance. All mutation operations create new instances * rather than modifying the existing map, ensuring immutability. * * **Implementation Details:** * - Uses ReadonlyMap<K, V> 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 keys. Must extend MapSetKeyType. * @template V The type of the values. * @implements IMap * @implements Iterable * @internal This class should not be used directly. Use IMap.create() instead. */ class IMapClass { #map; #showNotFoundMessage; /** * Constructs an IMapClass instance with the given entries. * * @param iterable An iterable of key-value pairs to populate the map. * @param showNotFoundMessage Whether to log warning messages when operations * are performed on non-existent keys. Useful for debugging. * Defaults to false for production use. * @internal Use IMap.create() instead of calling this constructor directly. */ constructor(iterable, showNotFoundMessage = false) { this.#map = new Map(iterable); this.#showNotFoundMessage = showNotFoundMessage; } /** @inheritdoc */ get size() { return asUint32(this.#map.size); } /** @inheritdoc */ has(key) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return this.#map.has(key); } /** @inheritdoc */ get(key) { if (!this.has(key)) return Optional.none; // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-non-null-assertion return Optional.some(this.#map.get(key)); } /** @inheritdoc */ every(predicate) { for (const [k, v] of this.entries()) { if (!predicate(v, k)) return false; } return true; } /** @inheritdoc */ some(predicate) { for (const [k, v] of this.entries()) { if (predicate(v, k)) return true; } return false; } /** @inheritdoc */ delete(key) { if (!this.has(key)) { if (this.#showNotFoundMessage) { const keyStr = unknownToString(key); console.warn(`IMap.delete: key not found: ${keyStr}`); } return this; } return IMap.create(Array.from(this.#map).filter(([k]) => !Object.is(k, key))); } /** @inheritdoc */ set(key, value) { if (value === this.get(key)) return this; // has no changes if (!this.has(key)) { return IMap.create([...this.#map, tp(key, value)]); } else { return IMap.create(Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? value : v))); } } /** @inheritdoc */ update(key, updater) { const curr = this.get(key); if (Optional.isNone(curr)) { if (this.#showNotFoundMessage) { const keyStr = unknownToString(key); console.warn(`IMap.update: key not found: ${keyStr}`); } return this; } return IMap.create(Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? updater(curr.value) : v))); } /** @inheritdoc */ withMutations(actions) { const mut_result = new Map(this.#map); for (const action of actions) { switch (action.type) { case 'delete': mut_result.delete(action.key); break; case 'set': mut_result.set(action.key, action.value); break; case 'update': { const { key } = action; const curr = mut_result.get(key); if (!mut_result.has(key) || curr === undefined) { if (this.#showNotFoundMessage) { const keyStr = unknownToString(key); console.warn(`IMap.withMutations: key not found: ${keyStr}`); } break; } mut_result.set(key, action.updater(curr)); break; } } } return IMap.create(mut_result); } /** @inheritdoc */ map(mapFn) { return IMap.create(this.toArray().map(([k, v]) => tp(k, mapFn(v, k)))); } /** @inheritdoc */ mapKeys(mapFn) { return IMap.create(this.toArray().map(([k, v]) => tp(mapFn(k), v))); } /** @inheritdoc */ mapEntries(mapFn) { return IMap.create(this.toArray().map(mapFn)); } /** @inheritdoc */ forEach(callbackfn) { for (const [key, value] of this.#map.entries()) { callbackfn(value, key); } } /** * @inheritdoc */ [Symbol.iterator]() { return this.#map[Symbol.iterator](); } /** @inheritdoc */ keys() { return this.#map.keys(); } /** @inheritdoc */ values() { return this.#map.values(); } /** @inheritdoc */ entries() { return this.#map.entries(); } /** @inheritdoc */ toKeysArray() { return Array.from(this.keys()); } /** @inheritdoc */ toValuesArray() { return Array.from(this.values()); } /** @inheritdoc */ toEntriesArray() { return Array.from(this.entries()); } /** @inheritdoc */ toArray() { return Array.from(this.entries()); } /** @inheritdoc */ toRawMap() { return this.#map; } } export { IMap }; //# sourceMappingURL=imap.mjs.map