import type { ProxyContext } from "../../ProxyContext"; import { _getUnderlyingNode } from "../../types"; import { getNodeFromRawObject } from "../../util/getNodeFromRaw"; import { nodeToString } from "../../util/NodeSet"; import type { RawValue } from "../../util/RawObject"; import type { LdSet } from "./LdSet"; /* eslint-disable @typescript-eslint/no-explicit-any */ export class BasicLdSet = NonNullable> extends Set implements LdSet { protected context: ProxyContext; private hashMap = new Map(); constructor(proxyContext: ProxyContext) { super(); this.context = proxyContext; } private hashFn(value: T) { if (typeof value !== "object") return value.toString(); return nodeToString(getNodeFromRawObject(value, this.context.contextUtil)); } /** * =========================================================================== * Base Set Functions * =========================================================================== */ add(value: T): this { const key = this.hashFn(value); if (!this.hashMap.has(key)) { this.hashMap.set(key, value); super.add(value); } return this; } clear(): void { this.hashMap.clear(); super.clear(); } delete(value: T): boolean { const key = this.hashFn(value); if (this.hashMap.has(key)) { this.hashMap.delete(key); return super.delete(value); } return false; } has(value: T): boolean { const key = this.hashFn(value); return this.hashMap.has(key); } /** * =========================================================================== * Array Functions * =========================================================================== */ every( predicate: (value: T, set: LdSet) => value is S, thisArg?: any, ): this is LdSet; every( predicate: (value: T, set: LdSet) => unknown, thisArg?: any, ): boolean; every(predicate: (value: T, set: LdSet) => any, thisArg?: any): boolean { for (const value of this) { if (!predicate.call(thisArg, value, this)) return false; } return true; } some( predicate: (value: T, set: LdSet) => unknown, thisArg?: any, ): boolean { for (const value of this) { if (predicate.call(thisArg, value, this)) return true; } return false; } forEach( callbackfn: (value: T, value2: T, set: LdSet) => void, thisArg?: any, ): void { for (const value of this) { callbackfn.call(thisArg, value, value, this); } } map(callbackfn: (value: T, set: LdSet) => U, thisArg?: any): U[] { const returnValues: U[] = []; for (const value of this) { returnValues.push(callbackfn.call(thisArg, value, this)); } return returnValues; } filter( predicate: (value: T, set: LdSet) => value is S, thisArg?: any, ): LdSet; filter( predicate: (value: T, set: LdSet) => unknown, thisArg?: any, ): LdSet; filter( predicate: (value: T, set: LdSet) => any, thisArg?: unknown, ): LdSet { const newSet = new BasicLdSet(this.context); for (const value of this) { if (predicate.call(thisArg, value, this)) newSet.add(value); } return newSet; } reduce( callbackfn: (previousValue: T, currentValue: T, set: LdSet) => T, ): T; reduce( callbackfn: (previousValue: T, currentValue: T, set: LdSet) => T, initialValue: T, ): T; reduce( callbackfn: (previousValue: U, currentValue: T, set: LdSet) => U, initialValue: U, ): U; reduce(callbackfn: any, initialValue?: any): any { const iterator = this[Symbol.iterator](); let accumulator; if (initialValue === undefined) { const first = iterator.next(); if (first.done) { throw new TypeError("Reduce of empty collection with no initial value"); } accumulator = first.value; } else { accumulator = initialValue; } let result = iterator.next(); while (!result.done) { accumulator = callbackfn(accumulator, result.value, this); result = iterator.next(); } return accumulator; } /** * =========================================================================== * Set Methods * =========================================================================== */ difference(other: Set): LdSet { return this.filter((value) => !other.has(value)); } intersection(other: Set): LdSet { const newSet = new BasicLdSet(this.context); const iteratingSet = this.size < other.size ? this : other; const comparingSet = this.size < other.size ? other : this; for (const value of iteratingSet) { if (comparingSet.has(value)) { newSet.add(value); } } return newSet; } isDisjointFrom(other: Set): boolean { const iteratingSet = this.size < other.size ? this : other; const comparingSet = this.size < other.size ? other : this; for (const value of iteratingSet) { if (comparingSet.has(value)) return false; } return true; } isSubsetOf(other: Set): boolean { if (this.size > other.size) return false; for (const value of this) { if (!other.has(value)) return false; } return true; } isSupersetOf(other: Set): boolean { if (this.size < other.size) return false; for (const value of other) { if (!this.has(value)) return false; } return true; } symmetricDifference(other: Set): LdSet { const newSet = new BasicLdSet(this.context); this.forEach((value) => newSet.add(value)); other.forEach((value) => { if (newSet.has(value)) { newSet.delete(value); } else { newSet.add(value); } }); return newSet; } union(other: Set): LdSet { const newSet = new BasicLdSet(this.context); this.forEach((value) => newSet.add(value)); other.forEach((value) => newSet.add(value)); return newSet; } }