/** * @since 2.0.0 */ import * as Equal from "../Equal.ts" import { format } from "../Formatter.ts" import * as Hash from "../Hash.ts" import type { Inspectable } from "../Inspectable.ts" import { NodeInspectSymbol, toJson } from "../Inspectable.ts" import type { Pipeable } from "../Pipeable.ts" import { pipeArguments } from "../Pipeable.ts" import { hasProperty } from "../Predicate.ts" import * as HashMap from "./hashMap.ts" /** @internal */ export const HashSetTypeId = "~effect/collections/HashSet" /** @internal */ export type HashSetTypeId = typeof HashSetTypeId /** @internal */ export interface HashSet extends Iterable, Equal.Equal, Pipeable, Inspectable { readonly [HashSetTypeId]: HashSetTypeId } const HashSetProto: Omit, HashSetTypeId> = { [Hash.symbol](this: HashSet): number { return Hash.hash(HashSetTypeId) }, [Equal.symbol](this: HashSet, that: unknown): boolean { return isHashSet(that) && size(this) === size(that) && every(this, (value) => has(that, value)) }, [Symbol.iterator](this: HashSet): Iterator { return HashMap.keys(keyMap(this)) }, toString() { return `HashSet(${format(Array.from(this))})` }, toJSON() { return { _id: "HashSet", values: toJson(Array.from(this)) } }, [NodeInspectSymbol]() { return this.toJSON() }, pipe() { return pipeArguments(this, arguments) } } const makeImpl = (keyMap: HashMap.HashMap): HashSet => { const set = Object.create(HashSetProto) set[HashSetTypeId] = HashSetTypeId set.keyMap = keyMap return set } /** @internal */ export const isHashSet = (u: unknown): u is HashSet => hasProperty(u, HashSetTypeId) /** @internal */ export const keyMap = (self: HashSet): HashMap.HashMap => (self as any).keyMap /** @internal */ export const empty = (): HashSet => makeImpl(HashMap.empty()) /** @internal */ export const make = >( ...values: Values ): HashSet => fromIterable(values) /** @internal */ export const fromIterable = (values: Iterable): HashSet => { let map = HashMap.empty() for (const value of values) { map = HashMap.set(map, value, true) } return makeImpl(map) } /** @internal */ export const has = (self: HashSet, value: V): boolean => HashMap.has(keyMap(self), value) /** @internal */ export const add = (self: HashSet, value: V): HashSet => { const map = keyMap(self) return HashMap.has(map, value) ? self : makeImpl(HashMap.set(map, value, true)) } /** @internal */ export const remove = (self: HashSet, value: V): HashSet => { const map = keyMap(self) return HashMap.has(map, value) ? makeImpl(HashMap.remove(map, value)) : self } /** @internal */ export const size = (self: HashSet): number => HashMap.size(keyMap(self)) /** @internal */ export const isEmpty = (self: HashSet): boolean => HashMap.isEmpty(keyMap(self)) // Helper function for building new HashSets from iteration const fromPredicate = (self: HashSet, predicate: (value: V) => boolean): HashSet => { let result = HashMap.empty() for (const value of self) { if (predicate(value)) { result = HashMap.set(result, value, true) } } return makeImpl(result) } /** @internal */ export const union = (self: HashSet, that: HashSet): HashSet => { const map = keyMap(self) let result = map as HashMap.HashMap for (const value of that) { result = HashMap.set(result, value, true) } return makeImpl(result) } /** @internal */ export const intersection = (self: HashSet, that: HashSet): HashSet => { let result = HashMap.empty() for (const value of self) { if (has(that, value as any)) { result = HashMap.set(result, value as V0 & V1, true) } } return makeImpl(result) } /** @internal */ export const difference = (self: HashSet, that: HashSet): HashSet => fromPredicate(self, (value) => !has(that, value as any)) /** @internal */ export const isSubset = (self: HashSet, that: HashSet): boolean => { for (const value of self) { if (!has(that, value as any)) { return false } } return true } /** @internal */ export const map = (self: HashSet, f: (value: V) => U): HashSet => { let result = HashMap.empty() for (const value of self) { result = HashMap.set(result, f(value), true) } return makeImpl(result) } /** @internal */ export const filter = (self: HashSet, predicate: (value: V) => boolean): HashSet => fromPredicate(self, predicate) /** @internal */ export const some = (self: HashSet, predicate: (value: V) => boolean): boolean => { for (const value of self) { if (predicate(value)) { return true } } return false } /** @internal */ export const every = (self: HashSet, predicate: (value: V) => boolean): boolean => { for (const value of self) { if (!predicate(value)) { return false } } return true } /** @internal */ export const reduce = (self: HashSet, zero: U, f: (accumulator: U, value: V) => U): U => { let result = zero for (const value of self) { result = f(result, value) } return result }