ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 16.1 kB
JavaScript
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 ISetMapped. */
var ISetMapped;
(function (ISetMapped) {
/**
* Creates a new ISetMapped instance with custom element transformation
* functions.
*
* This factory function creates an immutable set that can use complex objects
* as elements by providing bidirectional transformation functions. The
* `toKey` function converts your custom element type to a primitive type that
* can be efficiently stored, while `fromKey` reconstructs the original
* element type for iteration and access.
*
* **Performance:** O(n) where n is the number of elements in the iterable.
*
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const set = ISetMapped.create<Point, string>(
* [
* { x: 1, tag: 'a' },
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ],
* toKey,
* fromKey,
* );
*
* assert.deepStrictEqual(Array.from(set), [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ]);
* ```
*
* @template K The type of the custom elements.
* @template KM The type of the mapped primitive keys.
* @param iterable An iterable of elements using the custom element type.
* @param toKey A function that converts a custom element `K` to a primitive
* key `KM`. This function must be deterministic and produce unique values
* for unique elements.
* @param fromKey A function that converts a primitive key `KM` back to the
* custom element `K`. This should be the inverse of `toKey`.
* @returns A new ISetMapped instance containing all unique elements from the
* iterable.
*/
ISetMapped.create = (iterable, toKey, fromKey) => new ISetMappedClass(iterable, toKey, fromKey);
/**
* Checks if two ISetMapped instances are structurally equal.
*
* Two ISetMapped instances are considered equal if they have the same size
* and contain exactly the same elements. The comparison is performed on the
* underlying mapped keys, so the transformation functions themselves don't
* need to be identical. Elements are compared based on their mapped key
* representations.
*
* **Performance:** O(n) where n is the size of the smaller set.
*
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const first = ISetMapped.create<Point, string>(
* [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ],
* toKey,
* fromKey,
* );
*
* const second = ISetMapped.create<Point, string>(
* [
* { x: 2, tag: 'b' },
* { x: 1, tag: 'a' },
* ],
* toKey,
* fromKey,
* );
*
* const third = ISetMapped.create<Point, string>(
* [{ x: 3, tag: 'c' }],
* toKey,
* fromKey,
* );
*
* assert.isTrue(ISetMapped.equal(first, second));
*
* assert.isFalse(ISetMapped.equal(first, third));
* ```
*
* @template K The type of the custom elements.
* @template KM The type of the mapped primitive keys.
* @param a The first ISetMapped instance to compare.
* @param b The second ISetMapped instance to compare.
* @returns `true` if the sets contain exactly the same elements, `false`
* otherwise.
*/
ISetMapped.equal = (a, b) => a.size === b.size && a.every((e) => b.has(e));
/**
* Computes the difference between two ISetMapped instances.
*
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const previous = ISetMapped.create<Point, string>(
* [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ],
* toKey,
* fromKey,
* );
*
* const current = ISetMapped.create<Point, string>(
* [
* { x: 2, tag: 'b' },
* { x: 3, tag: 'c' },
* ],
* toKey,
* fromKey,
* );
*
* const { added, deleted } = ISetMapped.diff(previous, current);
*
* assert.deepStrictEqual(Array.from(added), [{ x: 3, tag: 'c' }]);
*
* assert.deepStrictEqual(Array.from(deleted), [{ x: 1, tag: 'a' }]);
* ```
*
* @template K The type of the elements.
* @template KM The type of the mapped keys.
* @param oldSet The original set.
* @param newSet The new set.
* @returns An object containing sets of added and deleted elements.
*/
ISetMapped.diff = (oldSet, newSet) => ({
deleted: oldSet.subtract(newSet),
added: newSet.subtract(oldSet),
});
/**
* Computes the intersection of two ISetMapped instances.
*
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const left = ISetMapped.create<Point, string>(
* [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ],
* toKey,
* fromKey,
* );
*
* const right = ISetMapped.create<Point, string>(
* [{ x: 2, tag: 'b' }],
* toKey,
* fromKey,
* );
*
* const overlap = ISetMapped.intersection(left, right);
*
* assert.deepStrictEqual(Array.from(overlap), [{ x: 2, tag: 'b' }]);
* ```
*
* @template K The type of the elements.
* @template KM The type of the mapped keys.
* @param a The first set.
* @param b The second set.
* @returns A new ISetMapped instance representing the intersection.
*/
ISetMapped.intersection = (a, b) => a.intersect(b);
/**
* Computes the union of two ISetMapped instances.
*
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const left = ISetMapped.create<Point, string>(
* [{ x: 1, tag: 'a' }],
* toKey,
* fromKey,
* );
*
* const right = ISetMapped.create<Point, string>(
* [{ x: 2, tag: 'b' }],
* toKey,
* fromKey,
* );
*
* const combined = ISetMapped.union(left, right);
*
* assert.deepStrictEqual(Array.from(combined), [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ]);
* ```
*
* @template K The type of the elements.
* @template KM The type of the mapped keys.
* @param a The first set.
* @param b The second set.
* @returns A new ISetMapped instance representing the union.
*/
ISetMapped.union = (a, b) => a.union(b);
})(ISetMapped || (ISetMapped = {}));
/**
* Internal class implementation for ISetMapped providing immutable set
* operations with element transformation.
*
* This class implements the ISetMapped interface by maintaining a JavaScript
* Set with primitive keys internally while exposing an API that works with
* custom element types. The transformation between custom and primitive
* elements is handled transparently through the provided `toKey` and `fromKey`
* functions.
*
* **Implementation Details:**
*
* - Uses ReadonlySet<KM> internally where KM is the primitive key type
* - Stores transformation functions for bidirectional element conversion
* - Implements copy-on-write semantics for efficiency
* - Provides optional debug messaging for development
*
* @template K The type of the custom elements.
* @template KM The type of the mapped primitive keys.
* @implements ISetMapped
* @implements Iterable
* @internal This class should not be used directly. Use ISetMapped.create() instead.
*/
class ISetMappedClass {
#set;
#toKey;
#fromKey;
#showNotFoundMessage;
/**
* Constructs an ISetMappedClass instance with custom element transformation.
*
* @param iterable An iterable of elements using the custom element type K.
* @param toKey A function that converts a custom element K to a primitive key
* KM. Must be deterministic and produce unique values for unique elements.
* @param fromKey A function that converts a primitive key KM back to the
* custom element K. Should be the inverse of the toKey function.
* @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 ISetMapped.create() instead of calling this constructor directly.
*/
constructor(iterable, toKey, fromKey, showNotFoundMessage = false) {
this.#set = new Set(Array.from(iterable, toKey));
this.#toKey = toKey;
this.#fromKey = fromKey;
this.#showNotFoundMessage = showNotFoundMessage;
}
/** @inheritdoc */
get size() {
return asUint32(this.#set.size);
}
/** @inheritdoc */
get isEmpty() {
return this.size === 0;
}
/** @inheritdoc */
has(key) {
return this.#set.has(this.#toKey(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 ISetMapped.create([...this.#set, this.#toKey(key)].map(this.#fromKey), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
delete(key) {
if (!this.has(key)) {
if (this.#showNotFoundMessage) {
console.warn(`ISetMapped.delete: key not found: ${String(this.#toKey(key))}`);
}
return this;
}
const keyMapped = this.#toKey(key);
return ISetMapped.create(Array.from(this.#set)
.filter((k) => !Object.is(k, keyMapped))
.map(this.#fromKey), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
withMutations(actions) {
const mut_result = new Set(this.#set);
for (const action of actions) {
const key = this.#toKey(action.key);
switch (action.type) {
case 'delete':
mut_result.delete(key);
break;
case 'add':
mut_result.add(key);
break;
}
}
return ISetMapped.create(Array.from(mut_result, this.#fromKey), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
map(mapFn) {
return ISetMapped.create(this.toArray().map(mapFn), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
filter(predicate) {
return ISetMapped.create(this.toArray().filter(predicate), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
filterNot(predicate) {
return ISetMapped.create(this.toArray().filter((k) => !predicate(k)), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
forEach(callbackfn) {
for (const km of this.#set) {
callbackfn(this.#fromKey(km));
}
}
/** @inheritdoc */
isSubsetOf(set) {
return this.every((k) => set.has(k));
}
/** @inheritdoc */
isSupersetOf(set) {
return set.every((k) => this.has(k));
}
/** @inheritdoc */
subtract(set) {
return ISetMapped.create(this.toArray().filter((k) => !set.has(k)), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
intersect(set) {
return ISetMapped.create(this.toArray().filter((k) => set.has(k)), this.#toKey, this.#fromKey);
}
/** @inheritdoc */
union(set) {
return ISetMapped.create([...this.values(), ...set.values()], this.#toKey, this.#fromKey);
}
/**
* @example
*
* ```ts
* type Point = Readonly<{ x: number; tag: string }>;
*
* const toKey = (point: Point) => JSON.stringify(point);
*
* // eslint-disable-next-line total-functions/no-unsafe-type-assertion
* const fromKey = (key: string) => JSON.parse(key) as Point;
*
* const set = ISetMapped.create<Point, string>(
* [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ],
* toKey,
* fromKey,
* );
*
* const collected = Array.from(set);
*
* assert.deepStrictEqual(collected, [
* { x: 1, tag: 'a' },
* { x: 2, tag: 'b' },
* ]);
* ```
*
* @inheritdoc
*/
*[Symbol.iterator]() {
for (const k of this.keys()) {
yield k;
}
}
/** @inheritdoc */
*keys() {
for (const km of this.#set.keys()) {
yield this.#fromKey(km);
}
}
/** @inheritdoc */
*values() {
for (const km of this.#set.keys()) {
// JavaScript Set's values() is an alias for keys()
yield this.#fromKey(km);
}
}
/** @inheritdoc */
*entries() {
for (const km of this.#set.keys()) {
// JavaScript Set's entries() yields [value, value]
const a = this.#fromKey(km);
yield [a, a];
}
}
/** @inheritdoc */
toArray() {
return Array.from(this.values());
}
/** @inheritdoc */
toRawSet() {
return this.#set;
}
}
export { ISetMapped };
//# sourceMappingURL=iset-mapped.mjs.map