From 486abefba693fb44a44669ed7a77ec544d044af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20BECHER?= Date: Sat, 11 May 2024 16:46:47 +0200 Subject: [PATCH] change project structure to remove namespaces --- src/{async.ts => async/impl.ts} | 426 ++++++------------------- src/async/index.ts | 101 ++++++ src/async/types.ts | 133 ++++++++ src/{bitarray.ts => bitarray/impl.ts} | 92 ++---- src/bitarray/index.ts | 29 ++ src/bitarray/types.ts | 26 ++ src/collector.ts | 247 --------------- src/collector/impl.ts | 187 +++++++++++ src/collector/index.ts | 56 ++++ src/collector/types.ts | 5 + src/index.ts | 16 +- src/random.ts | 136 -------- src/random/index.ts | 162 ++++++++++ src/random/types.ts | 20 ++ src/{sync.ts => sync/impl.ts} | 438 +++++--------------------- src/sync/index.ts | 147 +++++++++ src/sync/types.ts | 135 ++++++++ 17 files changed, 1201 insertions(+), 1155 deletions(-) rename src/{async.ts => async/impl.ts} (77%) create mode 100644 src/async/index.ts create mode 100644 src/async/types.ts rename src/{bitarray.ts => bitarray/impl.ts} (77%) create mode 100644 src/bitarray/index.ts create mode 100644 src/bitarray/types.ts delete mode 100644 src/collector.ts create mode 100644 src/collector/impl.ts create mode 100644 src/collector/index.ts create mode 100644 src/collector/types.ts delete mode 100644 src/random.ts create mode 100644 src/random/index.ts create mode 100644 src/random/types.ts rename src/{sync.ts => sync/impl.ts} (77%) create mode 100644 src/sync/index.ts create mode 100644 src/sync/types.ts diff --git a/src/async.ts b/src/async/impl.ts similarity index 77% rename from src/async.ts rename to src/async/impl.ts index faa4b1d..9fce8f4 100644 --- a/src/async.ts +++ b/src/async/impl.ts @@ -1,252 +1,17 @@ -import { Enumerable } from "./sync.js"; -import { asAsyncGenerator, combineAsyncComparers, defaultArrayComparer, identity, isAsyncIterable, operatorCompare, strictEquals } from "./utils.js"; -import { createQueue } from "./queue.js"; -import { selectionSorter } from "./sorting.js"; -import { createAsyncEqualitySet } from "./equality-set.js"; -import { createAsyncEqualityMap } from "./equality-map.js"; -import { Collector } from "./collector.js"; -import { MaybeAsyncPredicate, MaybeAsyncConverter, MaybeAsyncIterable, MaybeAsyncEquater, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncComparer, Predicate, MaybeAsyncAction, MaybePromise } from "./types.js"; - -//#region interfaces - -export interface AsyncEnumerable extends AsyncIterable> { - iterator(): AsyncIterator>; - - apply(pipeline: (enumerable: AsyncEnumerable) => TResult): TResult; - - count(predicate?: MaybeAsyncPredicate): Promise; - nonEnumeratedCount(): Promise; - fastCount(): Promise; - maxCount(): Promise; - - select(selector: MaybeAsyncConverter): AsyncEnumerable; - selectMany(selector: MaybeAsyncConverter>): AsyncEnumerable; - - where(predicate: MaybeAsyncPredicate): AsyncEnumerable; - - groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; - groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; - - join(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable<[TElement, TOther]>; - join(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable; - - groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; - groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater): AsyncEnumerable; - - contains(obj: TElement, equater?: MaybeAsyncEquater): Promise; - - sequenceEquals(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): Promise; - - append(obj: TElement): AsyncEnumerable; - - prepend(obj: TElement): AsyncEnumerable; - - remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEquater): AsyncEnumerable; - - concat(...iterables: MaybeAsyncIterable[]): AsyncEnumerable; - - first(predicate?: MaybeAsyncPredicate): Promise; - firstOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; - - last(predicate?: MaybeAsyncPredicate): Promise; - lastOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; - - single(predicate?: MaybeAsyncPredicate): Promise; - singleOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; - - elementAt(index: number): Promise; - elementAtOrDefault(index: number, def?: TElement): Promise; - - aggregate(accumulator: MaybeAsyncAccumulator): Promise; - aggregate(accumulator: MaybeAsyncAccumulator, seed?: TAccumulator): Promise; - aggregate(accumulator: MaybeAsyncAccumulator, seed?: TAccumulator, resultSelector?: MaybeAsyncConverter): Promise; - - min(): Promise; - minBy(selector: MaybeAsyncConverter): Promise; - - max(): Promise; - maxBy(selector: MaybeAsyncConverter): Promise; - - order(comparer?: MaybeAsyncComparer): AsyncEnumerable; - orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncEnumerable; - - orderDescending(comparer?: MaybeAsyncComparer): AsyncEnumerable; - orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncEnumerable; - - distinct(equater?: MaybeAsyncEquater): AsyncEnumerable; - distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; - - union(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; - unionBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; - - except(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; - exceptBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; - - intersect(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; - intersectBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; - - all(predicate: MaybeAsyncPredicate): Promise; - any(predicate: MaybeAsyncPredicate): Promise; - any(): Promise; - none(predicate: Predicate): Promise; - none(): Promise; - - skip(n: number): AsyncEnumerable; - skipLast(n: number): AsyncEnumerable; - skipWhile(condition: MaybeAsyncPredicate): AsyncEnumerable; - - take(n: number): AsyncEnumerable; - takeLast(n: number): AsyncEnumerable; - takeWhile(condition: MaybeAsyncPredicate): AsyncEnumerable; - - peek(action: MaybeAsyncAction): AsyncEnumerable; - - forEach(action: MaybeAsyncAction): Promise; - - zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]>; - - indexex(): AsyncEnumerable<[number, TElement]>; - - reversed(): AsyncEnumerable; - - chunked(size: number): AsyncEnumerable; - - // random(options?: RandomOptions): TElement | undefined; - - cached(): AsyncEnumerable; - - toArray(): Promise; - toMap(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; - toSet(): Promise>; - toObject(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; - - collect(collector: Collector): Promise; -} - -export interface GroupedAsyncEnumerable extends AsyncEnumerable { - get key(): TKey; -} - -export interface OrderedAsyncEnumerable extends AsyncEnumerable { - get comparer(): MaybeAsyncComparer; - - thenSelf(comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; - - thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; - - thenSelfDescending(comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; - - thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; -} - -//#endregion - -//#region wrappers - -export namespace AsyncEnumerable { - export function asAsync(enumerable: Enumerable): AsyncEnumerable { - return new WrappedEnumerable(enumerable); - } - - export function wrap(iterable: MaybeAsyncIterable): AsyncEnumerable { - if (isAsyncEnumerable(iterable)) { - return iterable; - } - - if (isAsyncIterable(iterable)) { - return sequence(iterable); - } - - return asAsync(Enumerable.wrap(iterable)); - } - - export function sequence(iterable: AsyncIterable): AsyncEnumerable { - return new WrappedAsyncIterable(iterable); - } - - export function empty(): AsyncEnumerable { - return EmptyAsyncEnumerable.INSTANCE; - } - - export function single(obj: T | PromiseLike): AsyncEnumerable { - return new WrappedObjectAsync(obj); - } - - export function array(array: (T | PromiseLike)[]): AsyncEnumerable { - return new WrappedArrayAsync(array); - } - - export function arrayLike(arrayLike: ArrayLike<(T | PromiseLike)>): AsyncEnumerable { - return new WrappedArrayLikeAsync(arrayLike); - } - - export function of(...elements: (T | PromiseLike)[]): AsyncEnumerable { - switch (elements.length) { - case 0: - return empty(); - case 1: - return single(elements[0]); - default: - return array(elements); - } - } - - export function func(f: () => Promise): AsyncEnumerable { - return new FunctionAsyncEnumerable(f); - } - - export function generator(generator: () => AsyncGenerator): AsyncEnumerable { - return new GeneratorAsyncEnumerable(generator); - } - - export function range(max: number): AsyncEnumerable - export function range(min: number, max: number): AsyncEnumerable - export function range(min: number, max: number, step: number): AsyncEnumerable - export function range(a: number, b?: number, c?: number): AsyncEnumerable { - if (b === undefined) { - b = a; - a = 0; - } - - if (c === undefined) { - c = 1; - } - - return new RangeAsyncEnumerable(a, b, c); - } - - export function repeat(value: T, count?: number): AsyncEnumerable { - if (count == undefined) { - return new RepeatForeverAsyncEnumerable(value); - } - - if (count < 0) { - throw new Error("count < 0"); - } - - if (count === 0) { - return empty(); - } - - if (count === 1) { - return new WrappedObjectAsync(value); - } - - return new RepeatAsyncEnumerable(value, count); - } - - export function isAsyncEnumerable(obj: any): obj is AsyncEnumerable { - return obj instanceof AsyncEnumerableMarker; - } -} - -//#endregion - -//#region implementations - -class AsyncEnumerableMarker { - -} +import { Collector } from "../collector/types.js"; +import { createAsyncEqualityMap } from "../equality-map.js"; +import { createAsyncEqualitySet } from "../equality-set.js"; +import { createQueue } from "../queue.js"; +import { getRandomElementAsync } from "../random/index.js"; +import { AsyncRandomOptions } from "../random/types.js"; +import { selectionSorter } from "../sorting.js"; +import { Enumerable } from "../sync/types.js"; +import { MaybeAsyncConverter, MaybeAsyncIterable, MaybeAsyncPredicate, MaybeAsyncEquater, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncComparer, MaybeAsyncAction, MaybePromise, Predicate } from "../types.js"; +import { strictEquals, identity, operatorCompare, asAsyncGenerator, defaultArrayComparer, combineAsyncComparers } from "../utils.js"; +import { array, empty, wrap } from "./index.js"; +import { AsyncEnumerable, GroupedAsyncEnumerable, OrderedAsyncEnumerable } from "./types.js"; + +export class AsyncEnumerableMarker { } export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarker implements AsyncEnumerable { [Symbol.asyncIterator]() { @@ -276,11 +41,11 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke } join(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable { - return new JoinAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new JoinAsyncEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater): AsyncEnumerable { - return new GroupJoinAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new GroupJoinAsyncEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } async contains(obj: TElement, equater?: MaybeAsyncEquater) { @@ -302,7 +67,7 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke return true; } - const that = AsyncEnumerable.wrap(iterable); + const that = wrap(iterable); const thisCount = await this.nonEnumeratedCount(); const thatCount = await that.nonEnumeratedCount(); @@ -356,7 +121,7 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke const arr: AsyncEnumerable[] = [this]; for (const iterable of iterables) { - arr.push(AsyncEnumerable.wrap(iterable)); + arr.push(wrap(iterable)); } return new ConcatAsyncEnumerable(arr); @@ -700,27 +465,27 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke } union(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable { - return new UnionAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), equater); + return new UnionAsyncEnumerable(this, wrap(iterable), equater); } unionBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable { - return new UnionByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector, equater); + return new UnionByAsyncEnumerable(this, wrap(iterable), selector, equater); } except(iterable: MaybeAsyncIterable): AsyncEnumerable { - return new ExceptAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); + return new ExceptAsyncEnumerable(this, wrap(iterable)); } exceptBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncEnumerable { - return new ExceptByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector); + return new ExceptByAsyncEnumerable(this, wrap(iterable), selector); } intersect(iterable: MaybeAsyncIterable): AsyncEnumerable { - return new IntersectAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); + return new IntersectAsyncEnumerable(this, wrap(iterable)); } intersectBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncEnumerable { - return new IntersectByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector); + return new IntersectByAsyncEnumerable(this, wrap(iterable), selector); } async all(predicate: MaybeAsyncPredicate) { @@ -804,7 +569,7 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? AsyncEnumerable.empty() : new TakeAsyncEnumerable(this, n); + return n === 0 ? empty() : new TakeAsyncEnumerable(this, n); } takeLast(n: number): AsyncEnumerable { @@ -812,7 +577,7 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? AsyncEnumerable.empty() : new TakeLastAsyncEnumerable(this, n); + return n === 0 ? empty() : new TakeLastAsyncEnumerable(this, n); } takeWhile(predicate: MaybeAsyncPredicate): AsyncEnumerable { @@ -829,11 +594,11 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke } } - zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]> { - return new ZippedAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); + zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[Awaited, Awaited]> { + return new ZippedAsyncEnumerable(this, wrap(iterable)); } - indexex(): AsyncEnumerable<[number, TElement]> { + indexex(): AsyncEnumerable<[number, Awaited]> { return new IndexedAsyncEnumerable(this); } @@ -849,6 +614,10 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke return new ChunkedAsyncEnumerable(this, size); } + async random(options?: AsyncRandomOptions | undefined): Promise { + return (await getRandomElementAsync(this, options)).element; + } + cached(): AsyncEnumerable { return new CacheAsyncEnumerable(this); } @@ -910,7 +679,7 @@ export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarke } } -class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implements AsyncEnumerable { +export class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implements AsyncEnumerable { #enumerable: AsyncEnumerable; constructor(enumerable: AsyncEnumerable) { @@ -973,7 +742,7 @@ class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implement return this.#enumerable.groupBy(keySelector, elementSelector, keyComparer); } - join(iterable: MaybeAsyncIterable, firstKeySelector: (obj: TElement) => MaybePromise, secondKeySelector: (obj: TOther) => MaybePromise, resultSelector?: undefined, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable<[TElement, TOther]>; + join(iterable: MaybeAsyncIterable, firstKeySelector: (obj: TElement) => MaybePromise, secondKeySelector: (obj: TOther) => MaybePromise, resultSelector?: undefined, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable<[Awaited, Awaited]>; join(iterable: MaybeAsyncIterable, firstKeySelector: (obj: TElement) => MaybePromise, secondKeySelector: (obj: TOther) => MaybePromise, resultSelector: (first: TElement, second: TOther) => MaybePromise, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable; join(iterable: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#enumerable.join(iterable, firstKeySelector, secondKeySelector, resultSelector, keyComparer); @@ -1160,11 +929,11 @@ class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implement return this.#enumerable.forEach(action); } - zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]> { + zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[Awaited, Awaited]> { return this.#enumerable.zip(iterable); } - indexex(): AsyncEnumerable<[number, TElement]> { + indexex(): AsyncEnumerable<[number, Awaited]> { return this.#enumerable.indexex(); } @@ -1176,6 +945,10 @@ class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implement return this.#enumerable.chunked(size); } + random(options?: AsyncRandomOptions | undefined): Promise { + return this.#enumerable.random(options); + } + cached(): AsyncEnumerable { return this.#enumerable.cached(); } @@ -1201,7 +974,7 @@ class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implement } } -class GroupedAsyncEnumerableImpl extends DelegatedAsyncEnumerable implements GroupedAsyncEnumerable { +export class GroupedAsyncEnumerableImpl extends DelegatedAsyncEnumerable implements GroupedAsyncEnumerable { readonly #key: TKey; constructor(key: TKey, grouping: AsyncEnumerable) { @@ -1269,7 +1042,7 @@ abstract class BaseOrderedAsyncEnumerable extends BaseAsyncEnumerable< } } -class EmptyAsyncEnumerable extends BaseAsyncEnumerable { +export class EmptyAsyncEnumerable extends BaseAsyncEnumerable { static readonly INSTANCE = new EmptyAsyncEnumerable(); override async nonEnumeratedCount() { @@ -1281,7 +1054,7 @@ class EmptyAsyncEnumerable extends BaseAsyncEnumerable { } } -class RangeAsyncEnumerable extends BaseAsyncEnumerable { +export class RangeAsyncEnumerable extends BaseAsyncEnumerable { readonly #min: number; readonly #max: number; readonly #step: number; @@ -1305,7 +1078,7 @@ class RangeAsyncEnumerable extends BaseAsyncEnumerable { } } -class RepeatAsyncEnumerable extends BaseAsyncEnumerable { +export class RepeatAsyncEnumerable extends BaseAsyncEnumerable { readonly #value: T; readonly #count: number; @@ -1329,7 +1102,7 @@ class RepeatAsyncEnumerable extends BaseAsyncEnumerable { } } -class RepeatForeverAsyncEnumerable extends BaseAsyncEnumerable { +export class RepeatForeverAsyncEnumerable extends BaseAsyncEnumerable { readonly #value: T; constructor(value: T) { @@ -1349,7 +1122,7 @@ class RepeatForeverAsyncEnumerable extends BaseAsyncEnumerable { } } -class WrappedObjectAsync extends BaseAsyncEnumerable { +export class WrappedObjectAsync extends BaseAsyncEnumerable { readonly #obj: T | PromiseLike; constructor(obj: T | PromiseLike) { @@ -1367,7 +1140,7 @@ class WrappedObjectAsync extends BaseAsyncEnumerable { } } -class WrappedArrayAsync extends BaseAsyncEnumerable { +export class WrappedArrayAsync extends BaseAsyncEnumerable { readonly #array: (T | PromiseLike)[]; constructor(array: (T | PromiseLike)[]) { @@ -1385,7 +1158,7 @@ class WrappedArrayAsync extends BaseAsyncEnumerable { } } -class WrappedArrayLikeAsync extends BaseAsyncEnumerable { +export class WrappedArrayLikeAsync extends BaseAsyncEnumerable { readonly #arrayLike: ArrayLike>; constructor(arrayLike: ArrayLike>) { @@ -1405,7 +1178,7 @@ class WrappedArrayLikeAsync extends BaseAsyncEnumerable { } } -class WrappedAsyncIterable extends BaseAsyncEnumerable { +export class WrappedAsyncIterable extends BaseAsyncEnumerable { readonly #iterable: AsyncIterable; constructor(iterable: AsyncIterable) { @@ -1419,7 +1192,7 @@ class WrappedAsyncIterable extends BaseAsyncEnumerable { } } -class GeneratorAsyncEnumerable extends BaseAsyncEnumerable { +export class GeneratorAsyncEnumerable extends BaseAsyncEnumerable { readonly #generator: () => AsyncGenerator; constructor(generator: () => AsyncGenerator) { @@ -1433,7 +1206,7 @@ class GeneratorAsyncEnumerable extends BaseAsyncEnumerable { } } -class FunctionAsyncEnumerable extends BaseAsyncEnumerable { +export class FunctionAsyncEnumerable extends BaseAsyncEnumerable { readonly #f: () => MaybePromise; constructor(f: () => MaybePromise) { @@ -1453,7 +1226,7 @@ class FunctionAsyncEnumerable extends BaseAsyncEnumerable { } } -class WrappedEnumerable extends BaseAsyncEnumerable { +export class WrappedEnumerable extends BaseAsyncEnumerable { readonly #enumerable: Enumerable; constructor(enumerable: Enumerable) { @@ -1479,7 +1252,7 @@ class WrappedEnumerable extends BaseAsyncEnumerable { } } -class ConcatAsyncEnumerable extends BaseAsyncEnumerable { +export class ConcatAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerables: Iterable>; constructor(enumerables: Iterable>) { @@ -1511,7 +1284,7 @@ class ConcatAsyncEnumerable extends BaseAsyncEnumerable { } } -class DistinctAsyncEnumerable extends BaseAsyncEnumerable { +export class DistinctAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #equater: MaybeAsyncEquater | undefined; @@ -1533,7 +1306,7 @@ class DistinctAsyncEnumerable extends BaseAsyncEnumerable { } } -class DistinctByAsyncEnumerable extends BaseAsyncEnumerable { +export class DistinctByAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #selector: MaybeAsyncConverter; readonly #equater: MaybeAsyncEquater | undefined; @@ -1559,7 +1332,7 @@ class DistinctByAsyncEnumerable extends BaseAsyncEnumerable { } } -class FilterAsyncEnumerable extends BaseAsyncEnumerable { +export class FilterAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #predicate: MaybeAsyncPredicate; @@ -1579,7 +1352,7 @@ class FilterAsyncEnumerable extends BaseAsyncEnumerable { } } -class FlatMapperAsyncEnumerable extends BaseAsyncEnumerable { +export class FlatMapperAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #converter: MaybeAsyncConverter>; @@ -1597,7 +1370,7 @@ class FlatMapperAsyncEnumerable extends BaseAsyncEnumerable { } } -class IndexedAsyncEnumerable extends BaseAsyncEnumerable<[number, T]> { +export class IndexedAsyncEnumerable extends BaseAsyncEnumerable<[number, Awaited]> { readonly #enumerable: AsyncEnumerable; constructor(enumerable: AsyncEnumerable) { @@ -1610,12 +1383,12 @@ class IndexedAsyncEnumerable extends BaseAsyncEnumerable<[number, T]> { let i = 0; for await (const obj of this.#enumerable) { - yield [i++, obj] as [number, T]; + yield [i++, obj] as [number, Awaited]; } } } -class MapperAsyncEnumerable extends BaseAsyncEnumerable { +export class MapperAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #converter: MaybeAsyncConverter; @@ -1637,7 +1410,7 @@ class MapperAsyncEnumerable extends BaseAsyncEnumerable { } } -class SkipWhileAsyncEnumerable extends BaseAsyncEnumerable { +export class SkipWhileAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #predicate: MaybeAsyncPredicate; @@ -1670,7 +1443,7 @@ class SkipWhileAsyncEnumerable extends BaseAsyncEnumerable { } } -class SkipLastAsyncEnumerable extends BaseAsyncEnumerable { +export class SkipLastAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #n: number; @@ -1711,7 +1484,7 @@ class SkipLastAsyncEnumerable extends BaseAsyncEnumerable { } } -class SkipAsyncEnumerable extends BaseAsyncEnumerable { +export class SkipAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #n: number; @@ -1743,7 +1516,7 @@ class SkipAsyncEnumerable extends BaseAsyncEnumerable { } } -class TakeWhileAsyncEnumerable extends BaseAsyncEnumerable { +export class TakeWhileAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #predicate: MaybeAsyncPredicate; @@ -1765,7 +1538,7 @@ class TakeWhileAsyncEnumerable extends BaseAsyncEnumerable { } } -class TakeLastAsyncEnumerable extends BaseAsyncEnumerable { +export class TakeLastAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #n: number; @@ -1792,7 +1565,7 @@ class TakeLastAsyncEnumerable extends BaseAsyncEnumerable { } } -class TakeAsyncEnumerable extends BaseAsyncEnumerable { +export class TakeAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #n: number; @@ -1825,13 +1598,13 @@ class TakeAsyncEnumerable extends BaseAsyncEnumerable { } } -class OrderAsyncEnumerable extends BaseOrderedAsyncEnumerable { +export class OrderAsyncEnumerable extends BaseOrderedAsyncEnumerable { constructor(enumerable: AsyncEnumerable, descending: boolean, sorter?: MaybeAsyncComparer) { super(enumerable, sorter ?? defaultArrayComparer, descending); } } -class OrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { +export class OrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { constructor(enumerable: AsyncEnumerable, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { super(enumerable, OrderByAsyncEnumerable.#createSorter(selector, sorter), descending); } @@ -1842,13 +1615,13 @@ class OrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { } } -class ThenOrderAsyncEnumerable extends BaseOrderedAsyncEnumerable { +export class ThenOrderAsyncEnumerable extends BaseOrderedAsyncEnumerable { constructor(enumerable: OrderedAsyncEnumerable, descending: boolean, sorter?: MaybeAsyncComparer) { super(enumerable, combineAsyncComparers(enumerable.comparer ?? defaultArrayComparer, sorter ?? defaultArrayComparer), descending); } } -class ThenOrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { +export class ThenOrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { constructor(enumerable: OrderedAsyncEnumerable, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { super(enumerable, ThenOrderByAsyncEnumerable.#createCombinedSorter(enumerable.comparer, selector, sorter), descending); } @@ -1860,7 +1633,7 @@ class ThenOrderByAsyncEnumerable extends BaseOrderedAsyncEnumerable { } } -class AppendAsyncEnumerable extends BaseAsyncEnumerable { +export class AppendAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #obj: T; @@ -1882,7 +1655,7 @@ class AppendAsyncEnumerable extends BaseAsyncEnumerable { } } -class PrependAsyncEnumerable extends BaseAsyncEnumerable { +export class PrependAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #obj: T; @@ -1904,7 +1677,7 @@ class PrependAsyncEnumerable extends BaseAsyncEnumerable { } } -class PeekAsyncEnumerable extends BaseAsyncEnumerable { +export class PeekAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #action: MaybeAsyncAction; @@ -1927,7 +1700,7 @@ class PeekAsyncEnumerable extends BaseAsyncEnumerable { } } -class ZippedAsyncEnumerable extends BaseAsyncEnumerable<[T, U]> { +export class ZippedAsyncEnumerable extends BaseAsyncEnumerable<[Awaited, Awaited]> { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; @@ -1957,12 +1730,12 @@ class ZippedAsyncEnumerable extends BaseAsyncEnumerable<[T, U]> { return; } - yield [firstNext.value, secondNext.value] as [T, U]; + yield [firstNext.value, secondNext.value] as [Awaited, Awaited]; } } } -class UnionAsyncEnumerable extends BaseAsyncEnumerable { +export class UnionAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #equater: MaybeAsyncEquater | undefined; @@ -1994,7 +1767,7 @@ class UnionAsyncEnumerable extends BaseAsyncEnumerable { } } -class UnionByAsyncEnumerable extends BaseAsyncEnumerable { +export class UnionByAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #selector: MaybeAsyncConverter; @@ -2028,7 +1801,7 @@ class UnionByAsyncEnumerable extends BaseAsyncEnumerable { } } -class ExceptAsyncEnumerable extends BaseAsyncEnumerable { +export class ExceptAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #equater: MaybeAsyncEquater | undefined; @@ -2058,7 +1831,7 @@ class ExceptAsyncEnumerable extends BaseAsyncEnumerable { } } -class ExceptByAsyncEnumerable extends BaseAsyncEnumerable { +export class ExceptByAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #selector: MaybeAsyncConverter; @@ -2090,7 +1863,7 @@ class ExceptByAsyncEnumerable extends BaseAsyncEnumerable { } } -class IntersectAsyncEnumerable extends BaseAsyncEnumerable { +export class IntersectAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #equater: MaybeAsyncEquater | undefined; @@ -2120,7 +1893,7 @@ class IntersectAsyncEnumerable extends BaseAsyncEnumerable { } } -class IntersectByAsyncEnumerable extends BaseAsyncEnumerable { +export class IntersectByAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #selector: MaybeAsyncConverter; @@ -2152,7 +1925,7 @@ class IntersectByAsyncEnumerable extends BaseAsyncEnumerable { } } -class ReversedAsyncEnumerable extends BaseAsyncEnumerable { +export class ReversedAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; constructor(enumerable: AsyncEnumerable) { @@ -2174,7 +1947,7 @@ class ReversedAsyncEnumerable extends BaseAsyncEnumerable { } } -class GroupByAsyncEnumerable extends BaseAsyncEnumerable> { +export class GroupByAsyncEnumerable extends BaseAsyncEnumerable> { readonly #enumerable: AsyncEnumerable; readonly #keySelector: MaybeAsyncConverter; readonly #elementSelector: MaybeAsyncConverter; @@ -2204,12 +1977,12 @@ class GroupByAsyncEnumerable extends BaseAsyncEnumerabl } for await (const entry of groupings) { - yield new GroupedAsyncEnumerableImpl(entry[0], AsyncEnumerable.array(entry[1])); + yield new GroupedAsyncEnumerableImpl(entry[0], array(entry[1])); } } } -class ChunkedAsyncEnumerable extends BaseAsyncEnumerable { +export class ChunkedAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #size: number; @@ -2243,7 +2016,7 @@ class ChunkedAsyncEnumerable extends BaseAsyncEnumerable { } } -class JoinAsyncEnumerable extends BaseAsyncEnumerable { +export class JoinAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #firstKeySelector: MaybeAsyncConverter; @@ -2277,7 +2050,7 @@ class JoinAsyncEnumerable extends BaseAsyncEnumer } } -class GroupJoinAsyncEnumerable extends BaseAsyncEnumerable { +export class GroupJoinAsyncEnumerable extends BaseAsyncEnumerable { readonly #first: AsyncEnumerable; readonly #second: AsyncEnumerable; readonly #firstKeySelector: MaybeAsyncConverter; @@ -2314,12 +2087,12 @@ class GroupJoinAsyncEnumerable extends BaseAsyncE } // yield this.#resultSelector(firstObj, this.#second.where(secondObj => this.#keyComparer(firstKey, this.#secondKeySelector(secondObj)))); - yield this.#resultSelector(firstObj, AsyncEnumerable.array(secondObjs)); + yield this.#resultSelector(firstObj, array(secondObjs)); } } } -class RemoveAsyncEnumerable extends BaseAsyncEnumerable { +export class RemoveAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; readonly #obj: T; readonly #all: boolean; @@ -2354,7 +2127,7 @@ class RemoveAsyncEnumerable extends BaseAsyncEnumerable { } } -class CacheAsyncEnumerable extends BaseAsyncEnumerable { +export class CacheAsyncEnumerable extends BaseAsyncEnumerable { readonly #enumerable: AsyncEnumerable; #cache: T[] | undefined; @@ -2368,20 +2141,3 @@ class CacheAsyncEnumerable extends BaseAsyncEnumerable { yield* this.#cache ??= await this.#enumerable.toArray(); } } - -class AwaitedAsyncEnumerable extends BaseAsyncEnumerable { - readonly #enumerable: AsyncEnumerable; - #cache: T[] | undefined; - - constructor(enumerable: AsyncEnumerable) { - super(); - - this.#enumerable = enumerable; - } - - override async *iterator() { - yield* this.#cache ??= await this.#enumerable.toArray(); - } -} - -//#endregion diff --git a/src/async/index.ts b/src/async/index.ts new file mode 100644 index 0000000..924b489 --- /dev/null +++ b/src/async/index.ts @@ -0,0 +1,101 @@ +import { wrap as wrapSync } from "../sync/index.js"; +import { Enumerable } from "../sync/types.js"; +import { MaybeAsyncIterable } from "../types.js"; +import { isAsyncIterable } from "../utils.js"; +import { WrappedEnumerable, WrappedAsyncIterable, EmptyAsyncEnumerable, WrappedObjectAsync, WrappedArrayAsync, WrappedArrayLikeAsync, FunctionAsyncEnumerable, GeneratorAsyncEnumerable, RangeAsyncEnumerable, RepeatForeverAsyncEnumerable, RepeatAsyncEnumerable, AsyncEnumerableMarker } from "./impl.js"; +import { AsyncEnumerable } from "./types.js"; + +export function asAsync(enumerable: Enumerable): AsyncEnumerable { + return new WrappedEnumerable(enumerable); +} + +export function wrap(iterable: MaybeAsyncIterable): AsyncEnumerable { + if (isAsyncEnumerable(iterable)) { + return iterable; + } + + if (isAsyncIterable(iterable)) { + return sequence(iterable); + } + + return asAsync(wrapSync(iterable)); +} + +export function sequence(iterable: AsyncIterable): AsyncEnumerable { + return new WrappedAsyncIterable(iterable); +} + +export function empty(): AsyncEnumerable { + return EmptyAsyncEnumerable.INSTANCE; +} + +export function single(obj: T | PromiseLike): AsyncEnumerable { + return new WrappedObjectAsync(obj); +} + +export function array(array: (T | PromiseLike)[]): AsyncEnumerable { + return new WrappedArrayAsync(array); +} + +export function arrayLike(arrayLike: ArrayLike<(T | PromiseLike)>): AsyncEnumerable { + return new WrappedArrayLikeAsync(arrayLike); +} + +export function of(...elements: (T | PromiseLike)[]): AsyncEnumerable { + switch (elements.length) { + case 0: + return empty(); + case 1: + return single(elements[0]); + default: + return array(elements); + } +} + +export function func(f: () => Promise): AsyncEnumerable { + return new FunctionAsyncEnumerable(f); +} + +export function generator(generator: () => AsyncGenerator): AsyncEnumerable { + return new GeneratorAsyncEnumerable(generator); +} + +export function range(max: number): AsyncEnumerable +export function range(min: number, max: number): AsyncEnumerable +export function range(min: number, max: number, step: number): AsyncEnumerable +export function range(a: number, b?: number, c?: number): AsyncEnumerable { + if (b === undefined) { + b = a; + a = 0; + } + + if (c === undefined) { + c = 1; + } + + return new RangeAsyncEnumerable(a, b, c); +} + +export function repeat(value: T, count?: number): AsyncEnumerable { + if (count == undefined) { + return new RepeatForeverAsyncEnumerable(value); + } + + if (count < 0) { + throw new Error("count < 0"); + } + + if (count === 0) { + return empty(); + } + + if (count === 1) { + return new WrappedObjectAsync(value); + } + + return new RepeatAsyncEnumerable(value, count); +} + +export function isAsyncEnumerable(obj: any): obj is AsyncEnumerable { + return obj instanceof AsyncEnumerableMarker; +} diff --git a/src/async/types.ts b/src/async/types.ts new file mode 100644 index 0000000..7c1ed2d --- /dev/null +++ b/src/async/types.ts @@ -0,0 +1,133 @@ +import { Collector } from "../collector/types.js"; +import { AsyncRandomOptions } from "../random/types.js"; +import { MaybeAsyncPredicate, MaybeAsyncConverter, MaybeAsyncIterable, MaybeAsyncEquater, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncComparer, Predicate, MaybeAsyncAction } from "../types.js"; + +export interface AsyncEnumerable extends AsyncIterable> { + iterator(): AsyncIterator>; + + apply(pipeline: (enumerable: AsyncEnumerable) => TResult): TResult; + + count(predicate?: MaybeAsyncPredicate): Promise; + nonEnumeratedCount(): Promise; + fastCount(): Promise; + maxCount(): Promise; + + select(selector: MaybeAsyncConverter): AsyncEnumerable; + selectMany(selector: MaybeAsyncConverter>): AsyncEnumerable; + + where(predicate: MaybeAsyncPredicate): AsyncEnumerable; + + groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; + + join(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable<[Awaited, Awaited]>; + join(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable; + + groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncEnumerable>; + groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater): AsyncEnumerable; + + contains(obj: TElement, equater?: MaybeAsyncEquater): Promise; + + sequenceEquals(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): Promise; + + append(obj: TElement): AsyncEnumerable; + + prepend(obj: TElement): AsyncEnumerable; + + remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEquater): AsyncEnumerable; + + concat(...iterables: MaybeAsyncIterable[]): AsyncEnumerable; + + first(predicate?: MaybeAsyncPredicate): Promise; + firstOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; + + last(predicate?: MaybeAsyncPredicate): Promise; + lastOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; + + single(predicate?: MaybeAsyncPredicate): Promise; + singleOrDefault(predicate?: MaybeAsyncPredicate, def?: TElement): Promise; + + elementAt(index: number): Promise; + elementAtOrDefault(index: number, def?: TElement): Promise; + + aggregate(accumulator: MaybeAsyncAccumulator): Promise; + aggregate(accumulator: MaybeAsyncAccumulator, seed?: TAccumulator): Promise; + aggregate(accumulator: MaybeAsyncAccumulator, seed?: TAccumulator, resultSelector?: MaybeAsyncConverter): Promise; + + min(): Promise; + minBy(selector: MaybeAsyncConverter): Promise; + + max(): Promise; + maxBy(selector: MaybeAsyncConverter): Promise; + + order(comparer?: MaybeAsyncComparer): AsyncEnumerable; + orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncEnumerable; + + orderDescending(comparer?: MaybeAsyncComparer): AsyncEnumerable; + orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncEnumerable; + + distinct(equater?: MaybeAsyncEquater): AsyncEnumerable; + distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; + + union(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; + unionBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; + + except(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; + exceptBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; + + intersect(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): AsyncEnumerable; + intersectBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable; + + all(predicate: MaybeAsyncPredicate): Promise; + any(predicate: MaybeAsyncPredicate): Promise; + any(): Promise; + none(predicate: Predicate): Promise; + none(): Promise; + + skip(n: number): AsyncEnumerable; + skipLast(n: number): AsyncEnumerable; + skipWhile(condition: MaybeAsyncPredicate): AsyncEnumerable; + + take(n: number): AsyncEnumerable; + takeLast(n: number): AsyncEnumerable; + takeWhile(condition: MaybeAsyncPredicate): AsyncEnumerable; + + peek(action: MaybeAsyncAction): AsyncEnumerable; + + forEach(action: MaybeAsyncAction): Promise; + + zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[Awaited, Awaited]>; + + indexex(): AsyncEnumerable<[number, Awaited]>; + + reversed(): AsyncEnumerable; + + chunked(size: number): AsyncEnumerable; + + random(options?: AsyncRandomOptions): Promise; + + cached(): AsyncEnumerable; + + toArray(): Promise; + toMap(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; + toSet(): Promise>; + toObject(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; + + collect(collector: Collector): Promise; +} + +export interface GroupedAsyncEnumerable extends AsyncEnumerable { + get key(): TKey; +} + +export interface OrderedAsyncEnumerable extends AsyncEnumerable { + get comparer(): MaybeAsyncComparer; + + thenSelf(comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; + + thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; + + thenSelfDescending(comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; + + thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncEnumerable; +} diff --git a/src/bitarray.ts b/src/bitarray/impl.ts similarity index 77% rename from src/bitarray.ts rename to src/bitarray/impl.ts index 18ff3a9..817becf 100644 --- a/src/bitarray.ts +++ b/src/bitarray/impl.ts @@ -1,33 +1,6 @@ -import { Collector } from "./collector.js"; -import { Enumerable } from "./sync.js"; -import { asArray } from "./utils.js"; - -export interface BitArray extends Iterable { - readonly length: number; - - isFull(): boolean; - isEmpty(): boolean; - - get(index: number): boolean; - set(index: number, value: boolean): void; - fill(value: boolean): void; - - and(other: BitArray): BitArray; - or(other: BitArray): BitArray; - xor(other: BitArray): BitArray; - not(): BitArray; - - contains(other: BitArray): boolean; - intersects(other: BitArray): boolean; - - slice(offset: number, length: number): BitArray; - copy(): BitArray; - toArray(): boolean[]; - - equals(other: BitArray): boolean; - - toString(): string; -} +import { join } from "../collector/index.js"; +import { arrayLike, sequence } from "../sync/index.js"; +import { BitArray } from "./types.js"; const BYTE_SIZE = Uint8Array.BYTES_PER_ELEMENT * 8; const FULL_BYTE = getMask(BYTE_SIZE); @@ -81,7 +54,7 @@ function* getBytes(bits: Iterable) { } } -class BitArrayImpl implements BitArray { +export class BitArrayImpl implements BitArray { readonly #length: number; readonly #bits: Uint8Array; readonly #wholeBytes: number; @@ -224,16 +197,16 @@ class BitArrayImpl implements BitArray { this.#ensureSameSize(other); return other instanceof BitArrayImpl ? - Enumerable.arrayLike(this.#bits).zip(Enumerable.arrayLike(other.#bits)).all(([a, b]) => (a & b) === b) : - Enumerable.sequence(this).zip(Enumerable.sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); + arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b) : + sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); } public intersects(other: BitArray) { this.#ensureSameSize(other); return other instanceof BitArrayImpl ? - Enumerable.arrayLike(this.#bits).zip(Enumerable.arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0) : - Enumerable.sequence(this).zip(Enumerable.sequence(other)).any(([a, b]) => a && b); + arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0) : + sequence(this).zip(sequence(other)).any(([a, b]) => a && b); } public slice(offset: number, length: number) { @@ -247,19 +220,19 @@ class BitArrayImpl implements BitArray { } public toArray() { - return Enumerable.sequence(this).toArray(); + return sequence(this).toArray(); } public equals(other: BitArray) { - return other === this || other && (other instanceof BitArrayImpl ? Enumerable.arrayLike(this.#bits).sequenceEquals(Enumerable.arrayLike(other.#bits)) : Enumerable.sequence(this).sequenceEquals(Enumerable.sequence(other))); + return other === this || other && (other instanceof BitArrayImpl ? arrayLike(this.#bits).sequenceEquals(arrayLike(other.#bits)) : sequence(this).sequenceEquals(sequence(other))); } public toString() { - return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(Collector.join()); + return sequence(this).select(bit => bit ? '1' : '0').collect(join()); } } -class EmptyBitArray implements BitArray { +export class EmptyBitArray implements BitArray { [Symbol.iterator](): Iterator { return { next: () => ({ done: true, value: undefined }) @@ -353,7 +326,7 @@ class EmptyBitArray implements BitArray { } } -class BitArraySlice implements BitArray { +export class BitArraySlice implements BitArray { readonly #parent: BitArray; readonly #offset: number; readonly #length: number; @@ -465,13 +438,13 @@ class BitArraySlice implements BitArray { public contains(other: BitArray) { this.#ensureSameSize(other); - return Enumerable.sequence(this).zip(Enumerable.sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); + return sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); } public intersects(other: BitArray) { this.#ensureSameSize(other); - return Enumerable.sequence(this).zip(Enumerable.sequence(other)).any(([a, b]) => a && b); + return sequence(this).zip(sequence(other)).any(([a, b]) => a && b); } public slice(offset: number, length: number) { @@ -489,43 +462,14 @@ class BitArraySlice implements BitArray { } public toArray() { - return Enumerable.sequence(this).toArray(); + return sequence(this).toArray(); } public equals(other: BitArray) { - return other === this || other && Enumerable.sequence(this).sequenceEquals(Enumerable.sequence(other)); + return other === this || other && sequence(this).sequenceEquals(sequence(other)); } public toString() { - return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(Collector.join()); - } -} - -const emptyBitArray = new EmptyBitArray(); - -export namespace BitArray { - export const EMPTY = emptyBitArray; - - export function create(length: number): BitArray { - if (length < 0) { - throw new Error("length < 0"); - } - - return length === 0 ? EMPTY : new BitArrayImpl(length); - } - - export function from(bits: Iterable): BitArray { - const arr = asArray(bits); - const result = create(arr.length); - - for (let i = 0; i < arr.length; i++) { - result.set(i, arr[i]); - } - - return result; - } - - export function of(...bits: boolean[]): BitArray { - return from(bits); + return sequence(this).select(bit => bit ? '1' : '0').collect(join()); } } diff --git a/src/bitarray/index.ts b/src/bitarray/index.ts new file mode 100644 index 0000000..b2372f9 --- /dev/null +++ b/src/bitarray/index.ts @@ -0,0 +1,29 @@ +import { asArray } from "../utils.js"; +import { EmptyBitArray, BitArrayImpl } from "./impl.js"; +import { BitArray } from "./types.js"; + +const emptyBitArray = new EmptyBitArray(); +export const EMPTY = emptyBitArray; + +export function create(length: number): BitArray { + if (length < 0) { + throw new Error("length < 0"); + } + + return length === 0 ? EMPTY : new BitArrayImpl(length); +} + +export function from(bits: Iterable): BitArray { + const arr = asArray(bits); + const result = create(arr.length); + + for (let i = 0; i < arr.length; i++) { + result.set(i, arr[i]); + } + + return result; +} + +export function of(...bits: boolean[]): BitArray { + return from(bits); +} diff --git a/src/bitarray/types.ts b/src/bitarray/types.ts new file mode 100644 index 0000000..6d4f9f4 --- /dev/null +++ b/src/bitarray/types.ts @@ -0,0 +1,26 @@ +export interface BitArray extends Iterable { + readonly length: number; + + isFull(): boolean; + isEmpty(): boolean; + + get(index: number): boolean; + set(index: number, value: boolean): void; + fill(value: boolean): void; + + and(other: BitArray): BitArray; + or(other: BitArray): BitArray; + xor(other: BitArray): BitArray; + not(): BitArray; + + contains(other: BitArray): boolean; + intersects(other: BitArray): boolean; + + slice(offset: number, length: number): BitArray; + copy(): BitArray; + toArray(): boolean[]; + + equals(other: BitArray): boolean; + + toString(): string; +} diff --git a/src/collector.ts b/src/collector.ts deleted file mode 100644 index 4d85630..0000000 --- a/src/collector.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { Converter } from "./types.js"; - -export interface Collector { - initialize(): TAccumulator; - accumulate(accumulator: TAccumulator, element: TElement): void; - 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; - } - - initialize() { - return this.#initialize(); - } - - accumulate(accumulator: TAccumulator, element: TElement) { - this.#accumulate(accumulator, element); - } - - finalize(accumulator: TAccumulator): TResult { - return this.#finalize(accumulator); - } - } - - 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/collector/impl.ts b/src/collector/impl.ts new file mode 100644 index 0000000..cf43ee9 --- /dev/null +++ b/src/collector/impl.ts @@ -0,0 +1,187 @@ +import { Converter } from "../types.js"; +import { Collector } from "./types.js"; + +export 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; + } + + initialize() { + return this.#initialize(); + } + + accumulate(accumulator: TAccumulator, element: TElement) { + this.#accumulate(accumulator, element); + } + + finalize(accumulator: TAccumulator): TResult { + return this.#finalize(accumulator); + } +} + +export class ToArrayCollector implements Collector { + initialize(): TElement[] { + return []; + } + + accumulate(accumulator: TElement[], element: TElement) { + accumulator.push(element); + } + + finalize(accumulator: TElement[]) { + return accumulator; + } +} + +export 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 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 class ToSetCollector implements Collector, Set> { + initialize() { + return new Set(); + } + + accumulate(accumulator: Set, element: TElement) { + accumulator.add(element); + } + + finalize(accumulator: Set) { + return accumulator; + } +} + +export 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 class SumCollector implements Collector { + initialize() { + return { sum: 0 }; + } + + accumulate(accumulator: { sum: number }, element: number) { + accumulator.sum += element; + } + + finalize(accumulator: { sum: number }) { + return accumulator.sum; + } +} + +export class BigIntSumCollector implements Collector { + initialize() { + return { sum: 0n, }; + } + + accumulate(accumulator: { sum: bigint }, element: bigint) { + accumulator.sum += element; + } + + finalize(accumulator: { sum: bigint }) { + return accumulator.sum; + } +} + +export 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; + } +} + +export 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); + } +} diff --git a/src/collector/index.ts b/src/collector/index.ts new file mode 100644 index 0000000..2e9ef9b --- /dev/null +++ b/src/collector/index.ts @@ -0,0 +1,56 @@ +import { Converter } from "../types.js"; +import { SimpleCollector, ToArrayCollector, ToObjectCollector, ToMapCollector, ToSetCollector, JoinCollector, SumCollector, BigIntSumCollector, AverageCollector, BigIntAverageCollector } from "./impl.js"; +import { Collector } from "./types.js"; + +export function create(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, + finalize: (accumulator: TAccumulator) => TResult): Collector { + return new SimpleCollector(initialize, accumulate, finalize); +} + +const toArrayCollector = new ToArrayCollector(); + +export function toArray(): Collector { + return toArrayCollector; +} + +export function toObject(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToObjectCollector(keySelector, valueSelector); +} + +export function toMap(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToMapCollector(keySelector, valueSelector); +} + +const toSetCollector = new ToSetCollector(); + +export function toSet(): Collector> { + return toSetCollector; +} + +export function join(delimiter?: string, prefix?: string, suffix?: string): Collector { + return new JoinCollector(delimiter, prefix, suffix); +} + +const sumCollector = new SumCollector(); + +export function sum(): Collector { + return sumCollector; +} + +const bigintSumCollector = new BigIntSumCollector(); + +export function bigintSum(): Collector { + return bigintSumCollector; +} + +const averageCollector = new AverageCollector(); + +export function average(): Collector { + return averageCollector; +} + +const bigintAverageCollector = new BigIntAverageCollector(); + +export function bigintAverage(): Collector { + return bigintAverageCollector; +} diff --git a/src/collector/types.ts b/src/collector/types.ts new file mode 100644 index 0000000..9566f63 --- /dev/null +++ b/src/collector/types.ts @@ -0,0 +1,5 @@ +export interface Collector { + initialize(): TAccumulator; + accumulate(accumulator: TAccumulator, element: TElement): void; + finalize(accumulator: TAccumulator): TResult; +} diff --git a/src/index.ts b/src/index.ts index d70b577..3713778 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,12 @@ -export { BaseEnumerable, Enumerable, GroupedEnumerable, OrderedEnumerable } from "./sync.js"; -export { AsyncEnumerable, BaseAsyncEnumerable, GroupedAsyncEnumerable, OrderedAsyncEnumerable } from "./async.js"; -export { Collector } from "./collector.js"; -export { Random, RandomOptions } from "./random.js"; +export { BaseEnumerable, DelegatedEnumerable } from "./sync/impl.js"; +export * as Enumerables from "./sync/index.js"; +export * from "./sync/types.js"; +export { BaseAsyncEnumerable, DelegatedAsyncEnumerable } from "./async/impl.js"; +export * as AsyncEnumerables from "./async/index.js"; +export * from "./async/types.js"; +export * as Collectors from "./collector/index.js"; +export * from "./collector/types.js"; +export * as BitArrays from "./bitarray/index.js"; +export * from "./bitarray/types.js"; +export * as Random from "./random/index.js"; +export * from "./random/types.js"; diff --git a/src/random.ts b/src/random.ts deleted file mode 100644 index 7dba5af..0000000 --- a/src/random.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { BitArray } from "./bitarray.js"; -import { asArray } from "./utils.js"; - -export type ElementPredicate = (index: number, obj: T) => boolean; -export type ElementWeight = (index: number, obj: T) => number; -export type RandomGenerator = () => number; - -export interface RandomOptions { - predicate?: ElementPredicate; - weight?: ElementWeight; - random?: RandomGenerator; -}; - -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 - }); - - 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 - }; - } - - 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 - }; - } - - function _getRandomElement(sequence: Iterable, options: Required>) { - const { predicate, weight, random } = options; - - let result: T | undefined = undefined; - let resultIndex = -1; - let index = 0; - let weightAcc = 0.0; - - 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; - } - } - } - - 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(); - } - - 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/random/index.ts b/src/random/index.ts new file mode 100644 index 0000000..820424c --- /dev/null +++ b/src/random/index.ts @@ -0,0 +1,162 @@ +import { create as createBitArray } from "../bitarray/index.js"; +import { BitArray } from "../bitarray/types.js"; +import { asArray } from "../utils.js"; +import { AsyncRandomOptions, ElementPredicate, ElementWeight, RandomGenerator, RandomOptions } from "./types.js"; + +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 +}); + +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 + }; +} + +function withDefaultOptions(options: RandomOptions | undefined): Required>; +function withDefaultOptions(options: AsyncRandomOptions | undefined): Required>; +function withDefaultOptions(options: RandomOptions | AsyncRandomOptions | undefined): Required | AsyncRandomOptions> { + if (!options || options === defaultOptions) { + return defaultOptions; + } + + return { + predicate: options.predicate ?? defaultOptions.predicate, + weight: options.weight ?? defaultOptions.weight, + random: options.random ?? defaultOptions.random + }; +} + +function _getRandomElement(sequence: Iterable, options: Required>) { + const { predicate, weight, random } = options; + + let result: T | undefined = undefined; + let resultIndex = -1; + let index = 0; + let weightAcc = 0.0; + + 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; + } + } + } + + return { element: result, index: resultIndex }; +} + +export function getRandomElement(sequence: Iterable, options?: RandomOptions) { + return _getRandomElement(sequence, withDefaultOptions(options)); +} + +async function _getRandomElementAsync(sequence: AsyncIterable, options: Required>) { + const { predicate, weight, random } = options; + + let result: T | undefined = undefined; + let resultIndex = -1; + let index = 0; + let weightAcc = 0.0; + + for await (const element of sequence) { + const currentIndex = index++; + + if (await predicate(currentIndex, element)) { + const w = await weight(currentIndex, element); + + if (w <= 0.0) { + continue; + } + + weightAcc += w; + + if (random() * weightAcc < w) { + result = element; + resultIndex = currentIndex; + } + } + } + + return { element: result, index: resultIndex }; +} + +export async function getRandomElementAsync(sequence: AsyncIterable, options?: AsyncRandomOptions) { + return await _getRandomElementAsync(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 = createBitArray(length); + this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options)); + + this.reset(); + } + + 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/random/types.ts b/src/random/types.ts new file mode 100644 index 0000000..76f9d9b --- /dev/null +++ b/src/random/types.ts @@ -0,0 +1,20 @@ +import { MaybeAsyncFunction } from "../types.js"; + +export type ElementPredicate = (index: number, obj: T) => boolean; +export type ElementWeight = (index: number, obj: T) => number; +export type RandomGenerator = () => number; + +export interface RandomOptions { + predicate?: ElementPredicate; + weight?: ElementWeight; + random?: RandomGenerator; +}; + +export type MaybeAsyncElementPredicate = MaybeAsyncFunction>; +export type MaybeAsyncElementWeight = MaybeAsyncFunction>; + +export interface AsyncRandomOptions { + predicate?: MaybeAsyncElementPredicate; + weight?: MaybeAsyncElementWeight; + random?: RandomGenerator; +}; diff --git a/src/sync.ts b/src/sync/impl.ts similarity index 77% rename from src/sync.ts rename to src/sync/impl.ts index 0c9f6ea..1486965 100644 --- a/src/sync.ts +++ b/src/sync/impl.ts @@ -1,293 +1,15 @@ -import { createEqualitySet } from "./equality-set.js"; -import { createEqualityMap } from "./equality-map.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"; -import { Predicate, Converter, FilterPredicate, Equater, BiConverter, Accumulator, Comparer, Action } from "./types.js"; - -export interface Enumerable extends Iterable { - iterator(): Iterator; - - apply(pipeline: (enumerable: Enumerable) => TResult): TResult; - - count(predicate?: Predicate): number; - nonEnumeratedCount(): number; - fastCount(): number; - maxCount(): number; - - select(selector: Converter): Enumerable; - selectMany(selector: Converter>): Enumerable; - - where(predicate: FilterPredicate): Enumerable; - where(predicate: Predicate): Enumerable; - - groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: Equater): Enumerable>; - groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: Equater): Enumerable>; - - join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Enumerable<[TElement, TOther]>; - join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: Equater): Enumerable; - - groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Enumerable>; - groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: Equater): Enumerable; - - contains(obj: TElement, equater?: Equater): boolean; - - sequenceEquals(iterable: Iterable, equater?: Equater): boolean; - - append(obj: TElement): Enumerable; - - prepend(obj: TElement): Enumerable; - - remove(obj: TElement, all?: boolean, equater?: Equater): Enumerable; - - concat(...iterables: Iterable[]): Enumerable; - - first(predicate?: Predicate): TElement; - firstOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; - - last(predicate?: Predicate): TElement; - lastOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; - - single(predicate?: Predicate): TElement; - singleOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; - - elementAt(index: number): TElement; - elementAtOrDefault(index: number, def?: TElement): TElement | undefined; - - aggregate(accumulator: Accumulator): TElement; - aggregate(accumulator: Accumulator, seed?: TAccumulator): TAccumulator; - aggregate(accumulator: Accumulator, seed?: TAccumulator, resultSelector?: Converter): TResult; - - min(comparer?: Comparer): TElement; - minBy(selector: Converter, comparer?: Comparer): TElement; - - max(comparer?: Comparer): TElement; - maxBy(selector: Converter, comparer?: Comparer): TElement; - - order(comparer?: Comparer): OrderedEnumerable; - orderBy(selector: Converter, comparer?: Comparer): OrderedEnumerable; - - orderDescending(comparer?: Comparer): OrderedEnumerable; - orderByDescending(selector: Converter, comparer?: Comparer): OrderedEnumerable; - - distinct(equater?: Equater): Enumerable; - distinctBy(selector: Converter, equater?: Equater): Enumerable; - - union(iterable: Iterable, equater?: Equater): Enumerable; - unionBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; - - except(iterable: Iterable, equater?: Equater): Enumerable; - exceptBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; - - intersect(iterable: Iterable, equater?: Equater): Enumerable; - intersectBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; - - all(predicate: Predicate): boolean; - any(predicate: Predicate): boolean; - any(): boolean; - none(predicate: Predicate): boolean; - none(): boolean; - - skip(n: number): Enumerable; - skipLast(n: number): Enumerable; - skipWhile(condition: Predicate): Enumerable; - - take(n: number): Enumerable; - takeLast(n: number): Enumerable; - takeWhile(condition: Predicate): Enumerable; - - peek(action: Action): Enumerable; - - forEach(action: Action): void; - - zip(iterable: Iterable): Enumerable<[TElement, TOther]>; - - indexed(): Enumerable<[number, TElement]>; - - reversed(): Enumerable; - - chunked(size: number): Enumerable; - - random(options?: RandomOptions): TElement | undefined; - - cached(): Enumerable; - - asArray(): TElement[]; - toArray(): TElement[]; - toMap(keySelector: Converter, valueSelector: Converter): Map; - toSet(): Set; - toObject(keySelector: Converter, valueSelector: Converter): Record; - - collect(collector: Collector): TResult; -} - -export interface GroupedEnumerable extends Enumerable { - get key(): TKey; -} - -export interface OrderedEnumerable extends Enumerable { - get comparer(): Comparer | undefined; - - thenSelf(comparer?: Comparer): OrderedEnumerable; - - thenBy(selector: Converter, comparer?: Comparer): OrderedEnumerable; - - thenSelfDescending(comparer?: Comparer): OrderedEnumerable; - - thenByDescending(selector: Converter, comparer?: Comparer): OrderedEnumerable; -} - -export namespace Enumerable { - export function wrap(iterable: Iterable): Enumerable { - if (isEnumerable(iterable)) { - return iterable; - } - - if (Array.isArray(iterable)) { - return array(iterable); - } - - if (iterable instanceof Set) { - return set(iterable); - } - - if (iterable instanceof Map) { - return map(iterable) as unknown as Enumerable; - } - - return sequence(iterable); - } - - export function sequence(iterable: Iterable): Enumerable { - return new WrappedIterable(iterable); - } - - export function empty(): Enumerable { - return EmptyEnumerable.INSTANCE; - } - - export function single(obj: T): Enumerable { - return new WrappedObject(obj); - } - - export function array(array: T[]): Enumerable { - return new WrappedArray(array); - } - - export function arrayLike(arrayLike: ArrayLike): Enumerable { - return new WrappedArrayLike(arrayLike); - } - - export function set(set: Set): Enumerable { - return new WrappedSet(set); - } - - export function map(map: Map): Enumerable<[K, V]> { - return new WrappedMap(map); - } - - export function of(...elements: T[]): Enumerable { - switch (elements.length) { - case 0: - return empty(); - case 1: - return single(elements[0]); - default: - return array(elements); - } - } - - export function ofPropertyKeys(...elements: T[]): Enumerable { - return of(...elements); - } - - export function entries(o: Record | ArrayLike): Enumerable<[string, T]> { - return array(Object.entries(o)); - } - - export function keys(o: object): Enumerable { - return array(Object.keys(o)); - } - - export function func(f: () => T): Enumerable { - return new FunctionEnumerable(f); - } - - export function generator(generator: () => Iterable): Enumerable { - return new GeneratorEnumerable(generator); - } - - export function range(max: number): Enumerable - export function range(min: number, max: number): Enumerable - export function range(min: number, max: number, step: number): Enumerable - export function range(a: number, b?: number, c?: number): Enumerable { - if (b === undefined) { - b = a; - a = 0; - } - - if (c === undefined) { - c = 1; - } - - return new RangeEnumerable(a, b, c); - } - - export function bigintRange(max: bigint): Enumerable - export function bigintRange(min: bigint, max: bigint): Enumerable - export function bigintRange(min: bigint, max: bigint, step: bigint): Enumerable - export function bigintRange(a: bigint, b?: bigint, c?: bigint): Enumerable { - if (b === undefined) { - b = a; - a = 0n; - } - - if (c === undefined) { - c = 1n; - } - - return new BigIntRangeEnumerable(a, b, c); - } - - export function repeat(value: T, count?: number): Enumerable { - if (count == undefined) { - return new RepeatForeverEnumerable(value); - } - - if (count < 0) { - throw new RangeError(); - } - - if (count === 0) { - return empty(); - } - - if (count === 1) { - return new WrappedObject(value); - } - - return new RepeatEnumerable(value, count); - } - - export function randomSequence(random?: RandomGenerator): Enumerable { - return new FunctionEnumerable(random ?? Random.mathRandom); - } - - export function concat(...enumerables: Enumerable[]): Enumerable { - return new ConcatEnumerable(enumerables); - } - - export function isEnumerable(obj: any): obj is Enumerable { - return obj instanceof EnumerableMarker; - } -} - -//#region enumerable implementation - -class EnumerableMarker { - -} +import { Collector } from "../collector/types.js"; +import { createEqualityMap } from "../equality-map.js"; +import { createEqualitySet } from "../equality-set.js"; +import { createQueue } from "../queue.js"; +import { getRandomElement } from "../random/index.js"; +import { RandomOptions } from "../random/types.js"; +import { Converter, FilterPredicate, Equater, BiConverter, Predicate, Accumulator, Comparer, Action } from "../types.js"; +import { strictEquals, identity, operatorCompare, reverseComparer, wrapAsIterable, defaultArrayComparer, combineComparers } from "../utils.js"; +import { array, empty, wrap } from "./index.js"; +import { Enumerable, GroupedEnumerable, OrderedEnumerable } from "./types.js"; + +export class EnumerableMarker { } export abstract class BaseEnumerable extends EnumerableMarker implements Enumerable { [Symbol.iterator]() { @@ -317,11 +39,11 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: Equater): Enumerable { - return new JoinEnumerable(this, Enumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new JoinEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: Equater): Enumerable { - return new GroupJoinEnumerable(this, Enumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new GroupJoinEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } contains(obj: TElement, equater?: Equater) { @@ -343,7 +65,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen return true; } - const that = Enumerable.wrap(iterable); + const that = wrap(iterable); const thisCount = this.nonEnumeratedCount(); const thatCount = that.nonEnumeratedCount(); @@ -397,7 +119,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen const arr: Enumerable[] = [this]; for (const iterable of iterables) { - arr.push(Enumerable.wrap(iterable)); + arr.push(wrap(iterable)); } return new ConcatEnumerable(arr); @@ -741,27 +463,27 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } union(iterable: Iterable, equater?: Equater): Enumerable { - return new UnionEnumerable(this, Enumerable.wrap(iterable), equater); + return new UnionEnumerable(this, wrap(iterable), equater); } unionBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable { - return new UnionByEnumerable(this, Enumerable.wrap(iterable), selector, equater); + return new UnionByEnumerable(this, wrap(iterable), selector, equater); } except(iterable: Iterable): Enumerable { - return new ExceptEnumerable(this, Enumerable.wrap(iterable)); + return new ExceptEnumerable(this, wrap(iterable)); } exceptBy(iterable: Iterable, selector: Converter): Enumerable { - return new ExceptByEnumerable(this, Enumerable.wrap(iterable), selector); + return new ExceptByEnumerable(this, wrap(iterable), selector); } intersect(iterable: Iterable): Enumerable { - return new IntersectEnumerable(this, Enumerable.wrap(iterable)); + return new IntersectEnumerable(this, wrap(iterable)); } intersectBy(iterable: Iterable, selector: Converter): Enumerable { - return new IntersectByEnumerable(this, Enumerable.wrap(iterable), selector); + return new IntersectByEnumerable(this, wrap(iterable), selector); } all(predicate: Predicate) { @@ -845,7 +567,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? Enumerable.empty() : new TakeEnumerable(this, n); + return n === 0 ? empty() : new TakeEnumerable(this, n); } takeLast(n: number): Enumerable { @@ -853,7 +575,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? Enumerable.empty() : new TakeLastEnumerable(this, n); + return n === 0 ? empty() : new TakeLastEnumerable(this, n); } takeWhile(predicate: Predicate): Enumerable { @@ -871,7 +593,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } zip(iterable: Iterable): Enumerable<[TElement, TOther]> { - return new ZippedEnumerable(this, Enumerable.wrap(iterable)); + return new ZippedEnumerable(this, wrap(iterable)); } indexed(): Enumerable<[number, TElement]> { @@ -891,7 +613,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } random(options?: RandomOptions) { - return Random.getRandomElement(this, options).element; + return getRandomElement(this, options).element; } cached(): Enumerable { @@ -947,7 +669,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } } -class DelegatedEnumerable extends EnumerableMarker implements Enumerable { +export class DelegatedEnumerable extends EnumerableMarker implements Enumerable { #enumerable: Enumerable; constructor(enumerable: Enumerable) { @@ -1238,7 +960,7 @@ class DelegatedEnumerable extends EnumerableMarker implements Enumerab } } -class GroupedEnumerableImpl extends DelegatedEnumerable implements GroupedEnumerable { +export class GroupedEnumerableImpl extends DelegatedEnumerable implements GroupedEnumerable { readonly #key: TKey; constructor(key: TKey, grouping: Enumerable) { @@ -1252,7 +974,7 @@ class GroupedEnumerableImpl extends DelegatedEnumerable extends BaseEnumerable implements OrderedEnumerable { +export abstract class BaseOrderedEnumerable extends BaseEnumerable implements OrderedEnumerable { readonly #enumerable: Enumerable; readonly #sorter: Comparer | undefined; readonly #descending: boolean; @@ -1314,7 +1036,7 @@ abstract class BaseOrderedEnumerable extends BaseEnumerable } } -class EmptyEnumerable extends BaseEnumerable { +export class EmptyEnumerable extends BaseEnumerable { static readonly INSTANCE = new EmptyEnumerable(); override nonEnumeratedCount() { @@ -1328,7 +1050,7 @@ class EmptyEnumerable extends BaseEnumerable { override *iterator() { } } -class RangeEnumerable extends BaseEnumerable { +export class RangeEnumerable extends BaseEnumerable { readonly #min: number; readonly #max: number; readonly #step: number; @@ -1360,7 +1082,7 @@ class RangeEnumerable extends BaseEnumerable { } } -class BigIntRangeEnumerable extends BaseEnumerable { +export class BigIntRangeEnumerable extends BaseEnumerable { readonly #min: bigint; readonly #max: bigint; readonly #step: bigint; @@ -1392,7 +1114,7 @@ class BigIntRangeEnumerable extends BaseEnumerable { } } -class RepeatEnumerable extends BaseEnumerable { +export class RepeatEnumerable extends BaseEnumerable { readonly #value: T; readonly #count: number; @@ -1420,7 +1142,7 @@ class RepeatEnumerable extends BaseEnumerable { } } -class RepeatForeverEnumerable extends BaseEnumerable { +export class RepeatForeverEnumerable extends BaseEnumerable { readonly #value: T; constructor(value: T) { @@ -1444,7 +1166,7 @@ class RepeatForeverEnumerable extends BaseEnumerable { } } -class WrappedObject extends BaseEnumerable { +export class WrappedObject extends BaseEnumerable { readonly #obj: T; constructor(obj: T) { @@ -1466,7 +1188,7 @@ class WrappedObject extends BaseEnumerable { } } -class WrappedIterable extends BaseEnumerable { +export class WrappedIterable extends BaseEnumerable { readonly #iterable: Iterable; constructor(iterable: Iterable) { @@ -1480,7 +1202,7 @@ class WrappedIterable extends BaseEnumerable { } } -class WrappedArray extends BaseEnumerable { +export class WrappedArray extends BaseEnumerable { readonly #array: T[]; constructor(array: T[]) { @@ -1544,7 +1266,7 @@ class WrappedArray extends BaseEnumerable { } } -class WrappedArrayLike extends BaseEnumerable { +export class WrappedArrayLike extends BaseEnumerable { readonly #arrayLike: ArrayLike; constructor(arrayLike: ArrayLike) { @@ -1580,7 +1302,7 @@ class WrappedArrayLike extends BaseEnumerable { } } -class WrappedSet extends BaseEnumerable { +export class WrappedSet extends BaseEnumerable { readonly #set: Set; constructor(set: Set) { @@ -1610,7 +1332,7 @@ class WrappedSet extends BaseEnumerable { } } -class WrappedMap extends BaseEnumerable<[K, V]> { +export class WrappedMap extends BaseEnumerable<[K, V]> { readonly #map: Map; constructor(map: Map) { @@ -1644,7 +1366,7 @@ class WrappedMap extends BaseEnumerable<[K, V]> { } } -class GeneratorEnumerable extends BaseEnumerable { +export class GeneratorEnumerable extends BaseEnumerable { readonly #generator: () => Iterable; constructor(generator: () => Iterable) { @@ -1658,7 +1380,7 @@ class GeneratorEnumerable extends BaseEnumerable { } } -class FunctionEnumerable extends BaseEnumerable { +export class FunctionEnumerable extends BaseEnumerable { readonly #f: () => T; constructor(f: () => T) { @@ -1682,7 +1404,7 @@ class FunctionEnumerable extends BaseEnumerable { } } -class ConcatEnumerable extends BaseEnumerable { +export class ConcatEnumerable extends BaseEnumerable { readonly #enumerables: Iterable>; constructor(enumerables: Iterable>) { @@ -1734,7 +1456,7 @@ class ConcatEnumerable extends BaseEnumerable { } } -class DistinctEnumerable extends BaseEnumerable { +export class DistinctEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #equater: Equater | undefined; @@ -1760,7 +1482,7 @@ class DistinctEnumerable extends BaseEnumerable { } } -class DistinctByEnumerable extends BaseEnumerable { +export class DistinctByEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #selector: Converter; readonly #equater: Equater | undefined; @@ -1788,7 +1510,7 @@ class DistinctByEnumerable extends BaseEnumerable { } } -class WhereEnumerable extends BaseEnumerable { +export class WhereEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #predicate: FilterPredicate; @@ -1812,7 +1534,7 @@ class WhereEnumerable extends BaseEnumerab } } -class SelectManyEnumerable extends BaseEnumerable { +export class SelectManyEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #converter: Converter>; @@ -1830,7 +1552,7 @@ class SelectManyEnumerable extends BaseEnumerable { } } -class IndexedEnumerable extends BaseEnumerable<[number, T]> { +export class IndexedEnumerable extends BaseEnumerable<[number, T]> { readonly #enumerable: Enumerable; constructor(enumerable: Enumerable) { @@ -1856,7 +1578,7 @@ class IndexedEnumerable extends BaseEnumerable<[number, T]> { } } -class SelectEnumerable extends BaseEnumerable { +export class SelectEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #converter: Converter; @@ -1882,7 +1604,7 @@ class SelectEnumerable extends BaseEnumerable { } } -class SkipWhileEnumerable extends BaseEnumerable { +export class SkipWhileEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #predicate: Predicate; @@ -1911,7 +1633,7 @@ class SkipWhileEnumerable extends BaseEnumerable { } } -class SkipLastEnumerable extends BaseEnumerable { +export class SkipLastEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #n: number; @@ -1956,7 +1678,7 @@ class SkipLastEnumerable extends BaseEnumerable { } } -class SkipEnumerable extends BaseEnumerable { +export class SkipEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #n: number; @@ -1992,7 +1714,7 @@ class SkipEnumerable extends BaseEnumerable { } } -class TakeWhileEnumerable extends BaseEnumerable { +export class TakeWhileEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #predicate: Predicate; @@ -2018,7 +1740,7 @@ class TakeWhileEnumerable extends BaseEnumerable { } } -class TakeLastEnumerable extends BaseEnumerable { +export class TakeLastEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #n: number; @@ -2049,7 +1771,7 @@ class TakeLastEnumerable extends BaseEnumerable { } } -class TakeEnumerable extends BaseEnumerable { +export class TakeEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #n: number; @@ -2086,13 +1808,13 @@ class TakeEnumerable extends BaseEnumerable { } } -class OrderEnumerable extends BaseOrderedEnumerable { +export class OrderEnumerable extends BaseOrderedEnumerable { constructor(enumerable: Enumerable, descending: boolean, sorter?: Comparer) { super(enumerable, sorter, descending); } } -class OrderByEnumerable extends BaseOrderedEnumerable { +export class OrderByEnumerable extends BaseOrderedEnumerable { constructor(enumerable: Enumerable, descending: boolean, selector: Converter, sorter?: Comparer) { super(enumerable, OrderByEnumerable.#createSorter(selector, sorter), descending); } @@ -2103,13 +1825,13 @@ class OrderByEnumerable extends BaseOrderedEnumerable { } } -class ThenOrderEnumerable extends BaseOrderedEnumerable { +export class ThenOrderEnumerable extends BaseOrderedEnumerable { constructor(enumerable: OrderedEnumerable, descending: boolean, sorter?: Comparer) { super(enumerable, combineComparers(enumerable.comparer ?? defaultArrayComparer, sorter ?? defaultArrayComparer), descending); } } -class ThenOrderByEnumerable extends BaseOrderedEnumerable { +export class ThenOrderByEnumerable extends BaseOrderedEnumerable { constructor(enumerable: OrderedEnumerable, descending: boolean, selector: Converter, sorter?: Comparer) { super(enumerable, ThenOrderByEnumerable.#createCombinedSorter(enumerable.comparer, selector, sorter), descending); } @@ -2121,7 +1843,7 @@ class ThenOrderByEnumerable extends BaseOrderedEnumerable { } } -class AppendEnumerable extends BaseEnumerable { +export class AppendEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #obj: T; @@ -2147,7 +1869,7 @@ class AppendEnumerable extends BaseEnumerable { } } -class PrependEnumerable extends BaseEnumerable { +export class PrependEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #obj: T; @@ -2173,7 +1895,7 @@ class PrependEnumerable extends BaseEnumerable { } } -class PeekEnumerable extends DelegatedEnumerable { +export class PeekEnumerable extends DelegatedEnumerable { readonly #action: Action; constructor(enumerable: Enumerable, action: Action) { @@ -2190,7 +1912,7 @@ class PeekEnumerable extends DelegatedEnumerable { } } -class ZippedEnumerable extends BaseEnumerable<[T, U]> { +export class ZippedEnumerable extends BaseEnumerable<[T, U]> { readonly #first: Enumerable; readonly #second: Enumerable; @@ -2229,7 +1951,7 @@ class ZippedEnumerable extends BaseEnumerable<[T, U]> { } } -class UnionEnumerable extends BaseEnumerable { +export class UnionEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #equater: Equater | undefined; @@ -2262,7 +1984,7 @@ class UnionEnumerable extends BaseEnumerable { } } -class UnionByEnumerable extends BaseEnumerable { +export class UnionByEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #selector: Converter; @@ -2297,7 +2019,7 @@ class UnionByEnumerable extends BaseEnumerable { } } -class ExceptEnumerable extends BaseEnumerable { +export class ExceptEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #equater: Equater | undefined; @@ -2329,7 +2051,7 @@ class ExceptEnumerable extends BaseEnumerable { } } -class ExceptByEnumerable extends BaseEnumerable { +export class ExceptByEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #selector: Converter; @@ -2363,7 +2085,7 @@ class ExceptByEnumerable extends BaseEnumerable { } } -class IntersectEnumerable extends BaseEnumerable { +export class IntersectEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #equater: Equater | undefined; @@ -2395,7 +2117,7 @@ class IntersectEnumerable extends BaseEnumerable { } } -class IntersectByEnumerable extends BaseEnumerable { +export class IntersectByEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #selector: Converter; @@ -2429,7 +2151,7 @@ class IntersectByEnumerable extends BaseEnumerable { } } -class ReversedEnumerable extends BaseEnumerable { +export class ReversedEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; constructor(enumerable: Enumerable) { @@ -2459,7 +2181,7 @@ class ReversedEnumerable extends BaseEnumerable { } } -class GroupByEnumerable extends BaseEnumerable> { +export class GroupByEnumerable extends BaseEnumerable> { readonly #enumerable: Enumerable; readonly #keySelector: Converter; readonly #elementSelector: Converter; @@ -2493,12 +2215,12 @@ class GroupByEnumerable extends BaseEnumerable extends BaseEnumerable { +export class ChunkedEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #size: number; @@ -2536,7 +2258,7 @@ class ChunkedEnumerable extends BaseEnumerable { } } -class JoinEnumerable extends BaseEnumerable { +export class JoinEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #firstKeySelector: Converter; @@ -2578,7 +2300,7 @@ class JoinEnumerable extends BaseEnumerable extends BaseEnumerable { +export class GroupJoinEnumerable extends BaseEnumerable { readonly #first: Enumerable; readonly #second: Enumerable; readonly #firstKeySelector: Converter; @@ -2622,12 +2344,12 @@ class GroupJoinEnumerable extends BaseEnumerable< } } - yield this.#resultSelector(firstObj, Enumerable.array(secondObjs)); + yield this.#resultSelector(firstObj, array(secondObjs)); } } } -class RemoveEnumerable extends BaseEnumerable { +export class RemoveEnumerable extends BaseEnumerable { readonly #enumerable: Enumerable; readonly #obj: T; readonly #all: boolean; @@ -2666,7 +2388,7 @@ class RemoveEnumerable extends BaseEnumerable { } } -class CacheEnumerable extends DelegatedEnumerable { +export class CacheEnumerable extends DelegatedEnumerable { #cached = false; constructor(enumerable: Enumerable) { @@ -2675,12 +2397,10 @@ class CacheEnumerable extends DelegatedEnumerable { override iterator() { if (!this.#cached) { - this.enumerable = Enumerable.array(this.enumerable.toArray()); + this.enumerable = array(this.enumerable.toArray()); this.#cached = true; } return super.iterator(); } } - -//#endregion diff --git a/src/sync/index.ts b/src/sync/index.ts new file mode 100644 index 0000000..7dcb325 --- /dev/null +++ b/src/sync/index.ts @@ -0,0 +1,147 @@ +import { mathRandom } from "../random/index.js"; +import { RandomGenerator } from "../random/types.js"; +import { BigIntRangeEnumerable, ConcatEnumerable, EmptyEnumerable, EnumerableMarker, FunctionEnumerable, GeneratorEnumerable, RangeEnumerable, RepeatEnumerable, RepeatForeverEnumerable, WrappedArray, WrappedArrayLike, WrappedIterable, WrappedMap, WrappedObject, WrappedSet } from "./impl.js"; +import { Enumerable } from "./types.js"; + +export function wrap(iterable: Iterable): Enumerable { + if (isEnumerable(iterable)) { + return iterable; + } + + if (Array.isArray(iterable)) { + return array(iterable); + } + + if (iterable instanceof Set) { + return set(iterable); + } + + if (iterable instanceof Map) { + return map(iterable) as unknown as Enumerable; + } + + return sequence(iterable); +} + +export function sequence(iterable: Iterable): Enumerable { + return new WrappedIterable(iterable); +} + +export function empty(): Enumerable { + return EmptyEnumerable.INSTANCE; +} + +export function single(obj: T): Enumerable { + return new WrappedObject(obj); +} + +export function array(array: T[]): Enumerable { + return new WrappedArray(array); +} + +export function arrayLike(arrayLike: ArrayLike): Enumerable { + return new WrappedArrayLike(arrayLike); +} + +export function set(set: Set): Enumerable { + return new WrappedSet(set); +} + +export function map(map: Map): Enumerable<[K, V]> { + return new WrappedMap(map); +} + +export function of(...elements: T[]): Enumerable { + switch (elements.length) { + case 0: + return empty(); + case 1: + return single(elements[0]); + default: + return array(elements); + } +} + +export function ofPropertyKeys(...elements: T[]): Enumerable { + return of(...elements); +} + +export function entries(o: Record | ArrayLike): Enumerable<[string, T]> { + return array(Object.entries(o)); +} + +export function keys(o: object): Enumerable { + return array(Object.keys(o)); +} + +export function func(f: () => T): Enumerable { + return new FunctionEnumerable(f); +} + +export function generator(generator: () => Iterable): Enumerable { + return new GeneratorEnumerable(generator); +} + +export function range(max: number): Enumerable +export function range(min: number, max: number): Enumerable +export function range(min: number, max: number, step: number): Enumerable +export function range(a: number, b?: number, c?: number): Enumerable { + if (b === undefined) { + b = a; + a = 0; + } + + if (c === undefined) { + c = 1; + } + + return new RangeEnumerable(a, b, c); +} + +export function bigintRange(max: bigint): Enumerable +export function bigintRange(min: bigint, max: bigint): Enumerable +export function bigintRange(min: bigint, max: bigint, step: bigint): Enumerable +export function bigintRange(a: bigint, b?: bigint, c?: bigint): Enumerable { + if (b === undefined) { + b = a; + a = 0n; + } + + if (c === undefined) { + c = 1n; + } + + return new BigIntRangeEnumerable(a, b, c); +} + +export function repeat(value: T, count?: number): Enumerable { + if (count == undefined) { + return new RepeatForeverEnumerable(value); + } + + if (count < 0) { + throw new RangeError(); + } + + if (count === 0) { + return empty(); + } + + if (count === 1) { + return new WrappedObject(value); + } + + return new RepeatEnumerable(value, count); +} + +export function randomSequence(random?: RandomGenerator): Enumerable { + return new FunctionEnumerable(random ?? mathRandom); +} + +export function concat(...enumerables: Enumerable[]): Enumerable { + return new ConcatEnumerable(enumerables); +} + +export function isEnumerable(obj: any): obj is Enumerable { + return obj instanceof EnumerableMarker; +} diff --git a/src/sync/types.ts b/src/sync/types.ts new file mode 100644 index 0000000..8c51162 --- /dev/null +++ b/src/sync/types.ts @@ -0,0 +1,135 @@ +import { Collector } from "../collector/types.js"; +import { RandomOptions } from "../random/types.js"; +import { Predicate, Converter, FilterPredicate, Equater, BiConverter, Accumulator, Comparer, Action } from "../types.js"; + +export interface Enumerable extends Iterable { + iterator(): Iterator; + + apply(pipeline: (enumerable: Enumerable) => TResult): TResult; + + count(predicate?: Predicate): number; + nonEnumeratedCount(): number; + fastCount(): number; + maxCount(): number; + + select(selector: Converter): Enumerable; + selectMany(selector: Converter>): Enumerable; + + where(predicate: FilterPredicate): Enumerable; + where(predicate: Predicate): Enumerable; + + groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: Equater): Enumerable>; + groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: Equater): Enumerable>; + + join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Enumerable<[TElement, TOther]>; + join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: Equater): Enumerable; + + groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Enumerable>; + groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: Equater): Enumerable; + + contains(obj: TElement, equater?: Equater): boolean; + + sequenceEquals(iterable: Iterable, equater?: Equater): boolean; + + append(obj: TElement): Enumerable; + + prepend(obj: TElement): Enumerable; + + remove(obj: TElement, all?: boolean, equater?: Equater): Enumerable; + + concat(...iterables: Iterable[]): Enumerable; + + first(predicate?: Predicate): TElement; + firstOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; + + last(predicate?: Predicate): TElement; + lastOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; + + single(predicate?: Predicate): TElement; + singleOrDefault(predicate?: Predicate, def?: TElement): TElement | undefined; + + elementAt(index: number): TElement; + elementAtOrDefault(index: number, def?: TElement): TElement | undefined; + + aggregate(accumulator: Accumulator): TElement; + aggregate(accumulator: Accumulator, seed?: TAccumulator): TAccumulator; + aggregate(accumulator: Accumulator, seed?: TAccumulator, resultSelector?: Converter): TResult; + + min(comparer?: Comparer): TElement; + minBy(selector: Converter, comparer?: Comparer): TElement; + + max(comparer?: Comparer): TElement; + maxBy(selector: Converter, comparer?: Comparer): TElement; + + order(comparer?: Comparer): OrderedEnumerable; + orderBy(selector: Converter, comparer?: Comparer): OrderedEnumerable; + + orderDescending(comparer?: Comparer): OrderedEnumerable; + orderByDescending(selector: Converter, comparer?: Comparer): OrderedEnumerable; + + distinct(equater?: Equater): Enumerable; + distinctBy(selector: Converter, equater?: Equater): Enumerable; + + union(iterable: Iterable, equater?: Equater): Enumerable; + unionBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; + + except(iterable: Iterable, equater?: Equater): Enumerable; + exceptBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; + + intersect(iterable: Iterable, equater?: Equater): Enumerable; + intersectBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable; + + all(predicate: Predicate): boolean; + any(predicate: Predicate): boolean; + any(): boolean; + none(predicate: Predicate): boolean; + none(): boolean; + + skip(n: number): Enumerable; + skipLast(n: number): Enumerable; + skipWhile(condition: Predicate): Enumerable; + + take(n: number): Enumerable; + takeLast(n: number): Enumerable; + takeWhile(condition: Predicate): Enumerable; + + peek(action: Action): Enumerable; + + forEach(action: Action): void; + + zip(iterable: Iterable): Enumerable<[TElement, TOther]>; + + indexed(): Enumerable<[number, TElement]>; + + reversed(): Enumerable; + + chunked(size: number): Enumerable; + + random(options?: RandomOptions): TElement | undefined; + + cached(): Enumerable; + + asArray(): TElement[]; + toArray(): TElement[]; + toMap(keySelector: Converter, valueSelector: Converter): Map; + toSet(): Set; + toObject(keySelector: Converter, valueSelector: Converter): Record; + + collect(collector: Collector): TResult; +} + +export interface GroupedEnumerable extends Enumerable { + get key(): TKey; +} + +export interface OrderedEnumerable extends Enumerable { + get comparer(): Comparer | undefined; + + thenSelf(comparer?: Comparer): OrderedEnumerable; + + thenBy(selector: Converter, comparer?: Comparer): OrderedEnumerable; + + thenSelfDescending(comparer?: Comparer): OrderedEnumerable; + + thenByDescending(selector: Converter, comparer?: Comparer): OrderedEnumerable; +}