/** * Return a copy of the set, with the item excluded. */ export function excluding(set: ReadonlySet, item: T): ReadonlySet { if (set.has(item)) { const newSet = new Set(set); newSet.delete(item); return newSet; } else { return set; } } export const empty: ReadonlySet = new Set(); /** * Return a copy of the set, with the item included. */ export function including(set: ReadonlySet, item: T): ReadonlySet { return set.has(item) ? set : new Set(set).add(item); } /** * Return a copy of the set, with the item removed it is was present, else with * it added. */ export function toggled(set: ReadonlySet, item: T): ReadonlySet { return set.has(item) ? excluding(set, item) : including(set, item); } /** * Return the items in the set as an array. */ export function toArray(set: ReadonlySet): T[] { return Array.from(set.values()); } /** * Combine the items from two sets. */ export function union(a: ReadonlySet, b: ReadonlySet): ReadonlySet { let result = a; for (const item of b) { result = including(result, item); } return result; } export function subtraction(a: ReadonlySet, b: ReadonlySet): ReadonlySet { let result = a; for (const item of b) { result = excluding(result, item); } return result; } export function intersection(a: ReadonlySet, b: ReadonlySet): ReadonlySet { const result = new Set(); for (const item of a) { if (b.has(item)) { result.add(item); } } return result; } export function some(a: ReadonlySet, predicate: (item: T) => boolean): boolean { for (const item of a) { if (predicate(item)) { return true; } } return false; } export class CountedSet { constructor(private readonly counts = new Map()) {} public add(item: T): number { const count = this.counts.get(item); const newCount = count === undefined ? 1 : count + 1; this.counts.set(item, newCount); return newCount; } public remove(item: T): number { const count = this.counts.get(item); const newCount = count !== undefined ? count - 1 : 0; this.counts.set(item, newCount); return newCount; } public count(item: T): number { const count = this.counts.get(item); return count === undefined ? 0 : count; } public *filter(predicate: (item: T, count: number) => boolean): IterableIterator { for (const [item, count] of this.counts) { if (predicate(item, count)) { yield item; } } } public entries(): IterableIterator<[T, number]> { return this.counts.entries(); } public copy() { return new CountedSet(new Map(this.counts.entries())); } }