1
0

refactor modules into namespaces

This commit is contained in:
2024-05-09 18:11:50 +02:00
parent ab11074744
commit fccebc61b6
5 changed files with 336 additions and 332 deletions

View File

@@ -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());
}
}

View File

@@ -6,240 +6,242 @@ export interface Collector<TElement, TAccumulator, TResult> {
finalize(accumulator: TAccumulator): TResult;
}
class SimpleCollector<TElement, TAccumulator, TResult> implements Collector<TElement, TAccumulator, TResult> {
readonly #initialize: () => TAccumulator;
readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void;
readonly #finalize: (accumulator: TAccumulator) => TResult;
export namespace Collector {
class SimpleCollector<TElement, TAccumulator, TResult> implements Collector<TElement, TAccumulator, TResult> {
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<TElement, TAccumulator, TResult>(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult): Collector<TElement, TAccumulator, TResult> {
return new SimpleCollector(initialize, accumulate, finalize);
}
accumulate(accumulator: TAccumulator, element: TElement) {
this.#accumulate(accumulator, element);
class ToArrayCollector<TElement> implements Collector<TElement, TElement[], TElement[]> {
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<any>();
export function toArray<TElement>(): Collector<TElement, any, TElement[]> {
return toArrayCollector;
}
class ToObjectCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue>, Record<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return {} as Record<TKey, TValue>;
}
accumulate(accumulator: Record<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator[key] = value;
}
finalize(accumulator: Record<TKey, TValue>) {
return accumulator;
}
}
export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue>> {
return new ToObjectCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToMapCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue>, Map<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return new Map<TKey, TValue>();
}
accumulate(accumulator: Map<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator.set(key, value);
}
finalize(accumulator: Map<TKey, TValue>) {
return accumulator;
}
}
export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue>> {
return new ToMapCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToSetCollector<TElement> implements Collector<TElement, Set<TElement>, Set<TElement>> {
initialize() {
return new Set<TElement>();
}
accumulate(accumulator: Set<TElement>, element: TElement) {
accumulator.add(element);
}
finalize(accumulator: Set<TElement>) {
return accumulator;
}
}
const toSetCollector = new ToSetCollector<any>();
export function toSet<TElement>(): Collector<TElement, any, Set<TElement>> {
return toSetCollector;
}
class JoinCollector implements Collector<any, any[], string> {
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<any, any, string> {
return new JoinCollector(delimiter, prefix, suffix);
}
class SumCollector implements Collector<number, { sum: number }, number> {
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<number, any, number> {
return sumCollector;
}
class BigIntSumCollector implements Collector<bigint, { sum: bigint }, bigint> {
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<bigint, any, bigint> {
return bigintSumCollector;
}
class AverageCollector implements Collector<number, { count: number, sum: number }, number> {
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<number, any, number> {
return averageCollector;
}
class BigIntAverageCollector implements Collector<bigint, { count: number, sum: bigint }, bigint> {
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<bigint, any, bigint> {
return bigintAverageCollector;
}
}
export function create<TElement, TAccumulator, TResult>(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult): Collector<TElement, TAccumulator, TResult> {
return new SimpleCollector(initialize, accumulate, finalize);
}
class ToArrayCollector<TElement> implements Collector<TElement, TElement[], TElement[]> {
initialize(): TElement[] {
return [];
}
accumulate(accumulator: TElement[], element: TElement) {
accumulator.push(element);
}
finalize(accumulator: TElement[]) {
return accumulator;
}
}
const toArrayCollector = new ToArrayCollector<any>();
export function toArray<TElement>(): Collector<TElement, any, TElement[]> {
return toArrayCollector;
}
class ToObjectCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue>, Record<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return {} as Record<TKey, TValue>;
}
accumulate(accumulator: Record<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator[key] = value;
}
finalize(accumulator: Record<TKey, TValue>) {
return accumulator;
}
}
export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue>> {
return new ToObjectCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToMapCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue>, Map<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return new Map<TKey, TValue>();
}
accumulate(accumulator: Map<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator.set(key, value);
}
finalize(accumulator: Map<TKey, TValue>) {
return accumulator;
}
}
export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue>> {
return new ToMapCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToSetCollector<TElement> implements Collector<TElement, Set<TElement>, Set<TElement>> {
initialize() {
return new Set<TElement>();
}
accumulate(accumulator: Set<TElement>, element: TElement) {
accumulator.add(element);
}
finalize(accumulator: Set<TElement>) {
return accumulator;
}
}
const toSetCollector = new ToSetCollector<any>();
export function toSet<TElement>(): Collector<TElement, any, Set<TElement>> {
return toSetCollector;
}
class JoinCollector implements Collector<any, any[], string> {
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<any, any, string> {
return new JoinCollector(delimiter, prefix, suffix);
}
class SumCollector implements Collector<number, { sum: number }, number> {
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<number, any, number> {
return sumCollector;
}
class BigIntSumCollector implements Collector<bigint, { sum: bigint }, bigint> {
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<bigint, any, bigint> {
return bigintSumCollector;
}
class AverageCollector implements Collector<number, { count: number, sum: number }, number> {
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<number, any, number> {
return averageCollector;
}
class BigIntAverageCollector implements Collector<bigint, { count: number, sum: bigint }, bigint> {
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<bigint, any, bigint> {
return bigintAverageCollector;
}

View File

@@ -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";

View File

@@ -11,124 +11,126 @@ export interface RandomOptions<T = any> {
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<Required<RandomOptions>>({
predicate: alwaysTrue,
weight: weightOfOne,
random: mathRandom
});
const defaultOptions = Object.freeze<Required<RandomOptions>>({
predicate: alwaysTrue,
weight: weightOfOne,
random: mathRandom
});
function mergeOptions<T>(first: RandomOptions<T> | undefined, second: RandomOptions<T> | undefined): RandomOptions<T> | undefined {
if (!first) {
return second;
function mergeOptions<T>(first: RandomOptions<T> | undefined, second: RandomOptions<T> | undefined): RandomOptions<T> | 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<T>(options: RandomOptions<T> | undefined): Required<RandomOptions<T>> {
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<T>(sequence: Iterable<T>, options: Required<RandomOptions<T>>) {
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<T>(options: RandomOptions<T> | undefined): Required<RandomOptions<T>> {
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<T>(sequence: Iterable<T>, options: Required<RandomOptions<T>>) {
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<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
return _getRandomElement(sequence, withDefaultOptions(options));
}
export class RandomPicker<T> {
readonly #elements: Iterable<T>;
readonly #flags: BitArray;
readonly #options: Required<RandomOptions<T>>;
public constructor(elements: Iterable<T>, length: number, options?: RandomOptions<T>) {
this.#elements = elements;
this.#flags = BitArray.create(length);
this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options));
this.reset();
export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
return _getRandomElement(sequence, withDefaultOptions(options));
}
public static from<T>(iterable: Iterable<T>, options?: RandomOptions<T>) {
const arr = asArray(iterable);
return new this<T>(arr, arr.length, options);
}
export class RandomPicker<T> {
readonly #elements: Iterable<T>;
readonly #flags: BitArray;
readonly #options: Required<RandomOptions<T>>;
public static of<T>(options?: RandomOptions<T>, ...values: T[]) {
return new this<T>(values, values.length, options);
}
public constructor(elements: Iterable<T>, length: number, options?: RandomOptions<T>) {
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<T>(iterable: Iterable<T>, options?: RandomOptions<T>) {
const arr = asArray(iterable);
return new this<T>(arr, arr.length, options);
}
public static of<T>(options?: RandomOptions<T>, ...values: T[]) {
return new this<T>(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;
}
}
}

View File

@@ -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<number> {
return new FunctionEnumerable(random ?? mathRandom);
return new FunctionEnumerable(random ?? Random.mathRandom);
}
export function concat<T>(...enumerables: Enumerable<T>[]): Enumerable<T> {
@@ -899,7 +899,7 @@ export abstract class BaseEnumerable<TElement> extends EnumerableMarker implemen
}
random(options?: RandomOptions<TElement>) {
return getRandomElement(this, options).element;
return Random.getRandomElement(this, options).element;
}
cached(): Enumerable<TElement> {