diff --git a/src/bitarray.ts b/src/bitarray.ts index 495877c..1aef86f 100644 --- a/src/bitarray.ts +++ b/src/bitarray.ts @@ -1,4 +1,4 @@ -import { join } from "./collector.js"; +import { Collector } from "./collector.js"; import { Enumerable } from "./sync.js"; import { asArray, isDefined } from "./utils.js"; @@ -255,7 +255,7 @@ class BitArrayImpl implements BitArray { } public toString() { - return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(join()); + return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(Collector.join()); } } @@ -497,7 +497,7 @@ class BitArraySlice implements BitArray { } public toString() { - return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(join()); + return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(Collector.join()); } } diff --git a/src/collector.ts b/src/collector.ts index 4a324d2..3b5b736 100644 --- a/src/collector.ts +++ b/src/collector.ts @@ -6,240 +6,242 @@ export interface Collector { finalize(accumulator: TAccumulator): TResult; } -class SimpleCollector implements Collector { - readonly #initialize: () => TAccumulator; - readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void; - readonly #finalize: (accumulator: TAccumulator) => TResult; +export namespace Collector { + class SimpleCollector implements Collector { + readonly #initialize: () => TAccumulator; + readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void; + readonly #finalize: (accumulator: TAccumulator) => TResult; - constructor(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, - finalize: (accumulator: TAccumulator) => TResult) { - this.#initialize = initialize; - this.#accumulate = accumulate; - this.#finalize = finalize; + constructor(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, + finalize: (accumulator: TAccumulator) => TResult) { + this.#initialize = initialize; + this.#accumulate = accumulate; + this.#finalize = finalize; + } + + initialize() { + return this.#initialize(); + } + + accumulate(accumulator: TAccumulator, element: TElement) { + this.#accumulate(accumulator, element); + } + + finalize(accumulator: TAccumulator): TResult { + return this.#finalize(accumulator); + } } - initialize() { - return this.#initialize(); + export function create(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, + finalize: (accumulator: TAccumulator) => TResult): Collector { + return new SimpleCollector(initialize, accumulate, finalize); } - accumulate(accumulator: TAccumulator, element: TElement) { - this.#accumulate(accumulator, element); + class ToArrayCollector implements Collector { + initialize(): TElement[] { + return []; + } + + accumulate(accumulator: TElement[], element: TElement) { + accumulator.push(element); + } + + finalize(accumulator: TElement[]) { + return accumulator; + } } - finalize(accumulator: TAccumulator): TResult { - return this.#finalize(accumulator); + const toArrayCollector = new ToArrayCollector(); + + export function toArray(): Collector { + return toArrayCollector; + } + + class ToObjectCollector implements Collector, Record> { + readonly #keySelector: Converter; + readonly #valueSelector: Converter; + + constructor(keySelector: Converter, valueSelector: Converter) { + this.#keySelector = keySelector; + this.#valueSelector = valueSelector; + } + + initialize() { + return {} as Record; + } + + accumulate(accumulator: Record, element: TElement) { + const key = this.#keySelector(element); + const value = this.#valueSelector(element); + + accumulator[key] = value; + } + + finalize(accumulator: Record) { + return accumulator; + } + } + + export function toObject(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToObjectCollector(keySelector, valueSelector); + } + + class ToMapCollector implements Collector, Map> { + readonly #keySelector: Converter; + readonly #valueSelector: Converter; + + constructor(keySelector: Converter, valueSelector: Converter) { + this.#keySelector = keySelector; + this.#valueSelector = valueSelector; + } + + initialize() { + return new Map(); + } + + accumulate(accumulator: Map, element: TElement) { + const key = this.#keySelector(element); + const value = this.#valueSelector(element); + + accumulator.set(key, value); + } + + finalize(accumulator: Map) { + return accumulator; + } + } + + export function toMap(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToMapCollector(keySelector, valueSelector); + } + + class ToSetCollector implements Collector, Set> { + initialize() { + return new Set(); + } + + accumulate(accumulator: Set, element: TElement) { + accumulator.add(element); + } + + finalize(accumulator: Set) { + return accumulator; + } + } + + const toSetCollector = new ToSetCollector(); + + export function toSet(): Collector> { + return toSetCollector; + } + + class JoinCollector implements Collector { + readonly #delimiter: string; + readonly #prefix: string; + readonly #suffix: string; + + constructor(delimiter?: string, prefix?: string, suffix?: string) { + this.#delimiter = delimiter ?? ""; + this.#prefix = prefix ?? ""; + this.#suffix = suffix ?? ""; + } + + initialize(): any[] { + return []; + } + + accumulate(accumulator: any[], element: string) { + accumulator.push(element); + } + + finalize(accumulator: any[]) { + return this.#prefix + accumulator.join(this.#delimiter) + this.#suffix; + } + } + + export function join(delimiter?: string, prefix?: string, suffix?: string): Collector { + return new JoinCollector(delimiter, prefix, suffix); + } + + class SumCollector implements Collector { + initialize() { + return { sum: 0 }; + } + + accumulate(accumulator: { sum: number }, element: number) { + accumulator.sum += element; + } + + finalize(accumulator: { sum: number }) { + return accumulator.sum; + } + } + + const sumCollector = new SumCollector(); + + export function sum(): Collector { + return sumCollector; + } + + class BigIntSumCollector implements Collector { + initialize() { + return { sum: 0n, }; + } + + accumulate(accumulator: { sum: bigint }, element: bigint) { + accumulator.sum += element; + } + + finalize(accumulator: { sum: bigint }) { + return accumulator.sum; + } + } + + const bigintSumCollector = new BigIntSumCollector(); + + export function bigintSum(): Collector { + return bigintSumCollector; + } + + class AverageCollector implements Collector { + initialize() { + return { count: 0, sum: 0 }; + } + + accumulate(accumulator: { count: number, sum: number }, element: number) { + accumulator.count++; + accumulator.sum += element; + } + + finalize(accumulator: { count: number, sum: number }) { + return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count; + } + } + + const averageCollector = new AverageCollector(); + + export function average(): Collector { + return averageCollector; + } + + class BigIntAverageCollector implements Collector { + initialize() { + return { count: 0, sum: 0n }; + } + + accumulate(accumulator: { count: number, sum: bigint }, element: bigint) { + accumulator.count++; + accumulator.sum += element; + } + + finalize(accumulator: { count: number, sum: bigint }) { + return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count); + } + } + + const bigintAverageCollector = new BigIntAverageCollector(); + + export function bigintAverage(): Collector { + return bigintAverageCollector; } } - -export function create(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, - finalize: (accumulator: TAccumulator) => TResult): Collector { - return new SimpleCollector(initialize, accumulate, finalize); -} - -class ToArrayCollector implements Collector { - initialize(): TElement[] { - return []; - } - - accumulate(accumulator: TElement[], element: TElement) { - accumulator.push(element); - } - - finalize(accumulator: TElement[]) { - return accumulator; - } -} - -const toArrayCollector = new ToArrayCollector(); - -export function toArray(): Collector { - return toArrayCollector; -} - -class ToObjectCollector implements Collector, Record> { - readonly #keySelector: Converter; - readonly #valueSelector: Converter; - - constructor(keySelector: Converter, valueSelector: Converter) { - this.#keySelector = keySelector; - this.#valueSelector = valueSelector; - } - - initialize() { - return {} as Record; - } - - accumulate(accumulator: Record, element: TElement) { - const key = this.#keySelector(element); - const value = this.#valueSelector(element); - - accumulator[key] = value; - } - - finalize(accumulator: Record) { - return accumulator; - } -} - -export function toObject(keySelector: Converter, valueSelector: Converter): Collector> { - return new ToObjectCollector(keySelector, valueSelector); -} - -class ToMapCollector implements Collector, Map> { - readonly #keySelector: Converter; - readonly #valueSelector: Converter; - - constructor(keySelector: Converter, valueSelector: Converter) { - this.#keySelector = keySelector; - this.#valueSelector = valueSelector; - } - - initialize() { - return new Map(); - } - - accumulate(accumulator: Map, element: TElement) { - const key = this.#keySelector(element); - const value = this.#valueSelector(element); - - accumulator.set(key, value); - } - - finalize(accumulator: Map) { - return accumulator; - } -} - -export function toMap(keySelector: Converter, valueSelector: Converter): Collector> { - return new ToMapCollector(keySelector, valueSelector); -} - -class ToSetCollector implements Collector, Set> { - initialize() { - return new Set(); - } - - accumulate(accumulator: Set, element: TElement) { - accumulator.add(element); - } - - finalize(accumulator: Set) { - return accumulator; - } -} - -const toSetCollector = new ToSetCollector(); - -export function toSet(): Collector> { - return toSetCollector; -} - -class JoinCollector implements Collector { - readonly #delimiter: string; - readonly #prefix: string; - readonly #suffix: string; - - constructor(delimiter?: string, prefix?: string, suffix?: string) { - this.#delimiter = delimiter ?? ""; - this.#prefix = prefix ?? ""; - this.#suffix = suffix ?? ""; - } - - initialize(): any[] { - return []; - } - - accumulate(accumulator: any[], element: string) { - accumulator.push(element); - } - - finalize(accumulator: any[]) { - return this.#prefix + accumulator.join(this.#delimiter) + this.#suffix; - } -} - -export function join(delimiter?: string, prefix?: string, suffix?: string): Collector { - return new JoinCollector(delimiter, prefix, suffix); -} - -class SumCollector implements Collector { - initialize() { - return { sum: 0 }; - } - - accumulate(accumulator: { sum: number }, element: number) { - accumulator.sum += element; - } - - finalize(accumulator: { sum: number }) { - return accumulator.sum; - } -} - -const sumCollector = new SumCollector(); - -export function sum(): Collector { - return sumCollector; -} - -class BigIntSumCollector implements Collector { - initialize() { - return { sum: 0n, }; - } - - accumulate(accumulator: { sum: bigint }, element: bigint) { - accumulator.sum += element; - } - - finalize(accumulator: { sum: bigint }) { - return accumulator.sum; - } -} - -const bigintSumCollector = new BigIntSumCollector(); - -export function bigintSum(): Collector { - return bigintSumCollector; -} - -class AverageCollector implements Collector { - initialize() { - return { count: 0, sum: 0 }; - } - - accumulate(accumulator: { count: number, sum: number }, element: number) { - accumulator.count++; - accumulator.sum += element; - } - - finalize(accumulator: { count: number, sum: number }) { - return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count; - } -} - -const averageCollector = new AverageCollector(); - -export function average(): Collector { - return averageCollector; -} - -class BigIntAverageCollector implements Collector { - initialize() { - return { count: 0, sum: 0n }; - } - - accumulate(accumulator: { count: number, sum: bigint }, element: bigint) { - accumulator.count++; - accumulator.sum += element; - } - - finalize(accumulator: { count: number, sum: bigint }) { - return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count); - } -} - -const bigintAverageCollector = new BigIntAverageCollector(); - -export function bigintAverage(): Collector { - return bigintAverageCollector; -} diff --git a/src/index.ts b/src/index.ts index 206eeda..5edb951 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ export * from "./sync.js"; export * from "./async.js"; -export * as collectors from "./collector.js"; -export * as random from "./random.js"; +export * from "./collector.js"; +export * from "./random.js"; diff --git a/src/random.ts b/src/random.ts index f693253..7dba5af 100644 --- a/src/random.ts +++ b/src/random.ts @@ -11,124 +11,126 @@ export interface RandomOptions { random?: RandomGenerator; }; -export const alwaysTrue: ElementPredicate = () => true; -export const weightOfOne: ElementWeight = () => 1.0; -export const mathRandom: RandomGenerator = () => Math.random(); +export namespace Random { + export const alwaysTrue: ElementPredicate = () => true; + export const weightOfOne: ElementWeight = () => 1.0; + export const mathRandom: RandomGenerator = () => Math.random(); -const defaultOptions = Object.freeze>({ - predicate: alwaysTrue, - weight: weightOfOne, - random: mathRandom -}); + const defaultOptions = Object.freeze>({ + predicate: alwaysTrue, + weight: weightOfOne, + random: mathRandom + }); -function mergeOptions(first: RandomOptions | undefined, second: RandomOptions | undefined): RandomOptions | undefined { - if (!first) { - return second; + function mergeOptions(first: RandomOptions | undefined, second: RandomOptions | undefined): RandomOptions | undefined { + if (!first) { + return second; + } + + if (!second) { + return first; + } + + const firstPredicate = first.predicate; + const secondPredicate = second.predicate; + + return { + predicate: firstPredicate ? secondPredicate ? (i, o) => firstPredicate(i, o) && secondPredicate(i, o) : firstPredicate : secondPredicate, + weight: first.weight ?? second.weight, + random: first.random ?? second.random + }; } - if (!second) { - return first; + function withDefaultOptions(options: RandomOptions | undefined): Required> { + if (!options || options === defaultOptions) { + return defaultOptions; + } + + return { + predicate: options.predicate ?? defaultOptions.predicate, + weight: options.weight ?? defaultOptions.weight, + random: options.random ?? defaultOptions.random + }; } - const firstPredicate = first.predicate; - const secondPredicate = second.predicate; + function _getRandomElement(sequence: Iterable, options: Required>) { + const { predicate, weight, random } = options; - return { - predicate: firstPredicate ? secondPredicate ? (i, o) => firstPredicate(i, o) && secondPredicate(i, o) : firstPredicate : secondPredicate, - weight: first.weight ?? second.weight, - random: first.random ?? second.random - }; -} + let result: T | undefined = undefined; + let resultIndex = -1; + let index = 0; + let weightAcc = 0.0; -function withDefaultOptions(options: RandomOptions | undefined): Required> { - if (!options || options === defaultOptions) { - return defaultOptions; - } + for (const element of sequence) { + const currentIndex = index++; - return { - predicate: options.predicate ?? defaultOptions.predicate, - weight: options.weight ?? defaultOptions.weight, - random: options.random ?? defaultOptions.random - }; -} + if (predicate(currentIndex, element)) { + const w = weight(currentIndex, element); -function _getRandomElement(sequence: Iterable, options: Required>) { - const { predicate, weight, random } = options; + if (w <= 0.0) { + continue; + } - let result: T | undefined = undefined; - let resultIndex = -1; - let index = 0; - let weightAcc = 0.0; + weightAcc += w; - for (const element of sequence) { - const currentIndex = index++; - - if (predicate(currentIndex, element)) { - const w = weight(currentIndex, element); - - if (w <= 0.0) { - continue; - } - - weightAcc += w; - - if (random() * weightAcc < w) { - result = element; - resultIndex = currentIndex; + if (random() * weightAcc < w) { + result = element; + resultIndex = currentIndex; + } } } + + return { element: result, index: resultIndex }; } - return { element: result, index: resultIndex }; -} - -export function getRandomElement(sequence: Iterable, options?: RandomOptions) { - return _getRandomElement(sequence, withDefaultOptions(options)); -} - -export class RandomPicker { - readonly #elements: Iterable; - readonly #flags: BitArray; - readonly #options: Required>; - - public constructor(elements: Iterable, length: number, options?: RandomOptions) { - this.#elements = elements; - this.#flags = BitArray.create(length); - this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options)); - - this.reset(); + export function getRandomElement(sequence: Iterable, options?: RandomOptions) { + return _getRandomElement(sequence, withDefaultOptions(options)); } - public static from(iterable: Iterable, options?: RandomOptions) { - const arr = asArray(iterable); - return new this(arr, arr.length, options); - } + export class RandomPicker { + readonly #elements: Iterable; + readonly #flags: BitArray; + readonly #options: Required>; - public static of(options?: RandomOptions, ...values: T[]) { - return new this(values, values.length, options); - } + public constructor(elements: Iterable, length: number, options?: RandomOptions) { + this.#elements = elements; + this.#flags = BitArray.create(length); + this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options)); - public get state() { - return this.#flags; - } - - public reset() { - this.#flags.fill(true); - } - - public next() { - const result = _getRandomElement(this.#elements, this.#options); - - if (result.index < 0) { this.reset(); - } else { - this.#flags.set(result.index, false); - - if (this.#flags.isEmpty()) { - this.reset(); - } } - return result.element; + public static from(iterable: Iterable, options?: RandomOptions) { + const arr = asArray(iterable); + return new this(arr, arr.length, options); + } + + public static of(options?: RandomOptions, ...values: T[]) { + return new this(values, values.length, options); + } + + public get state() { + return this.#flags; + } + + public reset() { + this.#flags.fill(true); + } + + public next() { + const result = _getRandomElement(this.#elements, this.#options); + + if (result.index < 0) { + this.reset(); + } else { + this.#flags.set(result.index, false); + + if (this.#flags.isEmpty()) { + this.reset(); + } + } + + return result.element; + } } } diff --git a/src/sync.ts b/src/sync.ts index c136900..92ade93 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1,6 +1,6 @@ import { createEqualitySet } from "./equality-set.js"; import { createEqualityMap } from "./equality-map.js"; -import { RandomGenerator, RandomOptions, getRandomElement, mathRandom } from "./random.js"; +import { RandomGenerator, RandomOptions, Random } from "./random.js"; import { createQueue } from "./queue.js"; import { Collector } from "./collector.js"; import { combineComparers, defaultArrayComparer, identity, operatorCompare, reverseComparer, strictEquals, wrapAsIterable } from "./utils.js"; @@ -279,7 +279,7 @@ export namespace Enumerable { } export function randomSequence(random?: RandomGenerator): Enumerable { - return new FunctionEnumerable(random ?? mathRandom); + return new FunctionEnumerable(random ?? Random.mathRandom); } export function concat(...enumerables: Enumerable[]): Enumerable { @@ -899,7 +899,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } random(options?: RandomOptions) { - return getRandomElement(this, options).element; + return Random.getRandomElement(this, options).element; } cached(): Enumerable {