ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 11.6 kB
JavaScript
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 '../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 IMapMapped. */
var IMapMapped;
(function (IMapMapped) {
/**
* Creates a new IMapMapped instance with custom key transformation functions.
*
* This factory function creates an immutable map that can use complex objects
* as keys by providing bidirectional transformation functions. The `toKey`
* function converts your custom key type to a primitive type that can be
* efficiently stored, while `fromKey` reconstructs the original key type for
* iteration and access.
*
* **Performance:** O(n) where n is the number of entries in the iterable.
*
* @template K The type of the custom keys.
* @template V The type of the values.
* @template KM The type of the mapped primitive keys.
* @param iterable An iterable of key-value pairs using the custom key type.
* @param toKey A function that converts a custom key `K` to a primitive key
* `KM`. This function must be deterministic and produce unique values for
* unique keys.
* @param fromKey A function that converts a primitive key `KM` back to the
* custom key `K`. This should be the inverse of `toKey`.
* @returns A new IMapMapped instance containing all entries from the
* iterable.
*/
IMapMapped.create = (iterable, toKey, fromKey) => new IMapMappedClass(iterable, toKey, fromKey);
/**
* Checks if two IMapMapped instances are structurally equal.
*
* Two IMapMapped instances are considered equal if they have the same size
* and contain exactly the same key-value pairs. The comparison is performed
* on the underlying mapped keys and values, so the transformation functions
* themselves don't need to be identical. 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 custom keys.
* @template V The type of the values.
* @template KM The type of the mapped primitive keys.
* @param a The first IMapMapped instance to compare.
* @param b The second IMapMapped instance to compare.
* @returns `true` if the maps contain exactly the same key-value pairs,
* `false` otherwise.
*/
IMapMapped.equal = (a, b) => a.size === b.size && a.every((v, k) => b.get(k) === v);
})(IMapMapped || (IMapMapped = {}));
/**
* Internal class implementation for IMapMapped providing immutable map
* operations with key transformation.
*
* This class implements the IMapMapped interface by maintaining a JavaScript
* Map with primitive keys internally while exposing an API that works with
* custom key types. The transformation between custom and primitive keys is
* handled transparently through the provided `toKey` and `fromKey` functions.
*
* **Implementation Details:**
*
* - Uses ReadonlyMap<KM, V> internally where KM is the primitive key type
* - Stores transformation functions for bidirectional key conversion
* - Implements copy-on-write semantics for efficiency
* - Provides optional debug messaging for development
*
* @template K The type of the custom keys.
* @template V The type of the values.
* @template KM The type of the mapped primitive keys.
* @implements IMapMapped
* @implements Iterable
* @internal This class should not be used directly. Use IMapMapped.create() instead.
*/
class IMapMappedClass {
#map;
#toKey;
#fromKey;
#showNotFoundMessage;
/**
* Constructs an IMapMappedClass instance with custom key transformation.
*
* @param iterable An iterable of key-value pairs using the custom key type K.
* @param toKey A function that converts a custom key K to a primitive key KM.
* Must be deterministic and produce unique values for unique keys.
* @param fromKey A function that converts a primitive key KM back to the
* custom key K. Should be the inverse of the toKey function.
* @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 IMapMapped.create() instead of calling this constructor directly.
*/
constructor(iterable, toKey, fromKey, showNotFoundMessage = false) {
this.#map = new Map(Array.from(iterable, ([k, v]) => [toKey(k), v]));
this.#toKey = toKey;
this.#fromKey = fromKey;
this.#showNotFoundMessage = showNotFoundMessage;
}
/** @inheritdoc */
get size() {
return asUint32(this.#map.size);
}
/** @inheritdoc */
has(key) {
return this.#map.has(this.#toKey(key));
}
/** @inheritdoc */
get(key) {
if (!this.has(key))
return none;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return some(this.#map.get(this.#toKey(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) {
console.warn(`IMapMapped.delete: key not found: ${String(this.#toKey(key))}`);
}
return this;
}
const keyMapped = this.#toKey(key);
return IMapMapped.create(Array.from(this.#map)
.filter(([km]) => !Object.is(km, keyMapped))
.map(([km, v]) => tp(this.#fromKey(km), v)), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
set(key, value) {
if (value === this.get(key))
return this; // has no changes
const keyMapped = this.#toKey(key);
if (!this.has(key)) {
return IMapMapped.create([...this.#map, tp(keyMapped, value)].map(([km, v]) => tp(this.#fromKey(km), v)), this.#toKey, this.#fromKey);
}
else {
return IMapMapped.create(Array.from(this.#map, ([km, v]) => tp(this.#fromKey(km), Object.is(km, keyMapped) ? value : v)), this.#toKey, this.#fromKey);
}
}
/** @inheritdoc */
update(key, updater) {
const curr = this.get(key);
if (isNone(curr)) {
if (this.#showNotFoundMessage) {
console.warn(`IMapMapped.update: key not found: ${String(this.#toKey(key))}`);
}
return this;
}
const keyMapped = this.#toKey(key);
return IMapMapped.create(Array.from(this.#map.entries(), (keyValue) => pipe(keyValue)
.map(([km, v]) => tp(km, Object.is(km, keyMapped) ? updater(curr.value) : v))
.map(([km, v]) => tp(this.#fromKey(km), v)).value), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
withMutations(actions) {
const mut_result = new Map(this.#map);
for (const action of actions) {
const key = this.#toKey(action.key);
switch (action.type) {
case 'delete':
mut_result.delete(key);
break;
case 'set':
mut_result.set(key, action.value);
break;
case 'update': {
const curr = mut_result.get(key);
if (!mut_result.has(key) || curr === undefined) {
if (this.#showNotFoundMessage) {
console.warn(`IMapMapped.withMutations::update: key not found: ${String(key)}`);
}
break;
}
mut_result.set(key, action.updater(curr));
break;
}
}
}
return IMapMapped.create(Array.from(mut_result, ([k, v]) => [this.#fromKey(k), v]), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
map(mapFn) {
return IMapMapped.create(this.toArray().map(([k, v]) => tp(k, mapFn(v, k))), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
mapKeys(mapFn) {
return IMapMapped.create(this.toArray().map(([k, v]) => tp(mapFn(k), v)), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
mapEntries(mapFn) {
return IMapMapped.create(this.toArray().map(mapFn), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
forEach(callbackfn) {
for (const [km, value] of this.#map.entries()) {
callbackfn(value, this.#fromKey(km));
}
}
/** @inheritdoc */
*[Symbol.iterator]() {
for (const e of this.entries()) {
yield e;
}
}
/** @inheritdoc */
*keys() {
for (const km of this.#map.keys()) {
yield this.#fromKey(km);
}
}
/** @inheritdoc */
*values() {
for (const v of this.#map.values()) {
yield v;
}
}
/** @inheritdoc */
*entries() {
for (const [km, v] of this.#map.entries()) {
yield [this.#fromKey(km), v];
}
}
/** @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 { IMapMapped };
//# sourceMappingURL=imap-mapped.mjs.map