export enum ImmutableContainerSetMode { INCLUDE, EXCLUDE } export class ImmutableContainerSet { public readonly mode: ImmutableContainerSetMode; private readonly set: Set; public constructor(set?: Iterable, mode?: ImmutableContainerSetMode) { this.mode = mode || ImmutableContainerSetMode.INCLUDE; this.set = new Set(set || []); } public static include(included?: Iterable) { return new ImmutableContainerSet(included, ImmutableContainerSetMode.INCLUDE); } public static exclude(excluded?: Iterable) { return new ImmutableContainerSet(excluded, ImmutableContainerSetMode.EXCLUDE); } public values(): T[] { return Array.from(this.set); } public add(value: T) { return this.update([ value ], this.mode === ImmutableContainerSetMode.INCLUDE); } public addIterable(values: Iterable) { return this.update(values, this.mode === ImmutableContainerSetMode.INCLUDE); } public remove(value: T) { return this.update([ value ], this.mode === ImmutableContainerSetMode.EXCLUDE); } public removeIterable(values: Iterable) { return this.update(values, this.mode === ImmutableContainerSetMode.EXCLUDE); } // Max is the maximum number of elements that could be contained at given time public size(max?: number) { if (this.mode === ImmutableContainerSetMode.INCLUDE) { return this.set.size; } else { if (max === undefined) { throw new Error('Requesting size of an ImmutableContainerSet with Mode:' + this.mode); } return max - this.set.size; } } public contains(value: T) { const inSet = this.set.has(value); return this.mode === ImmutableContainerSetMode.INCLUDE ? inSet : !inSet; } private update(values: Iterable, addToSet: boolean) { const updated = new ImmutableContainerSet(this.set, this.mode); if (addToSet) { for (const value of values) { updated.set.add(value); } } else { for (const value of values) { updated.set.delete(value); } } return updated; } }