ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 10.9 kB
JavaScript
import { isSome } from '../functional/optional/impl/optional-is-some.mjs';
import { none } from '../functional/optional/impl/optional-none.mjs';
import { some } from '../functional/optional/impl/optional-some.mjs';
import { isNone } from '../functional/optional/impl/optional-is-none.mjs';
import { pipe } from '../functional/pipe.mjs';
import '@sindresorhus/is';
import { tp } from '../others/tuple.mjs';
import { unknownToString } from '../others/unknown-to-string.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';
/** 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.
*
* @example
*
* ```ts
* const map = IMap.create<string, number | string>([
* ['id', 1],
* ['status', 'active'],
* ]);
*
* assert.isTrue(map.size === 2);
*
* assert.deepStrictEqual(map.get('status'), Optional.some('active'));
* ```
*
* @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.
*/
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.
*
* @example
*
* ```ts
* const first = IMap.create<'a' | 'b', number>([
* ['a', 1],
* ['b', 2],
* ]);
*
* const second = IMap.create<'a' | 'b', number>([
* ['b', 2],
* ['a', 1],
* ]);
*
* const third = IMap.create<'a' | 'b', number>([
* ['a', 1],
* ['b', 3],
* ]);
*
* assert.isTrue(IMap.equal(first, second));
*
* assert.isFalse(IMap.equal(first, third));
* ```
*
* @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.
*/
IMap.equal = (a, b) => a.size === b.size &&
a.every((v, k) => pipe(b.get(k)).map((v2) => isSome(v2) && v2.value === v).value);
})(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 Map<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 total-functions/no-unsafe-type-assertion
return this.#map.has(key);
}
/** @inheritdoc */
get(key) {
if (!this.has(key))
return none;
// eslint-disable-next-line total-functions/no-unsafe-type-assertion, @typescript-eslint/no-non-null-assertion
return 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) {
const curr = this.get(key);
if (isSome(curr) && value === curr.value)
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 (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);
}
}
/**
* @example
*
* ```ts
* const entries = [
* ['first', 1],
* ['second', 2],
* ] satisfies readonly (readonly [string, number])[];
*
* const map = IMap.create(entries);
*
* const collected = Array.from(map);
*
* assert.deepStrictEqual(collected, [
* ['first', 1],
* ['second', 2],
* ]);
* ```
*
* @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