From 6c0713d591159becc50d2258b8cbc1f2ddf3a76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20BECHER?= Date: Thu, 9 May 2024 17:35:55 +0200 Subject: [PATCH] harmonize sync and async implementations --- src/async.ts | 590 ++++++++++++++++++++++++++++++++++++------------ src/bitarray.ts | 26 +-- src/index.ts | 4 +- src/sync.ts | 276 +++++++++++----------- 4 files changed, 607 insertions(+), 289 deletions(-) diff --git a/src/async.ts b/src/async.ts index 9e57f02..77e5909 100644 --- a/src/async.ts +++ b/src/async.ts @@ -1,4 +1,4 @@ -import { Accumulator, Action, BiConverter, Comparer, Converter, Enumerable, Equater, Predicate, wrap as wrapSync } from "./sync.js"; +import { Accumulator, Action, BiConverter, Comparer, Converter, Enumerable, Equater, Predicate } from "./sync.js"; import { AsyncFunction, MaybeAsyncFunction, MaybePromise, asAsyncGenerator, combineAsyncComparers, defaultArrayComparer, identity, isAsyncIterable, operatorCompare, strictEquals } from "./utils.js"; import { createQueue } from "./queue.js"; import { selectionSorter } from "./sorting.js"; @@ -33,11 +33,12 @@ export type MaybeAsyncEquater = MaybeAsyncFunction>; export interface AsyncEnumerable extends AsyncIterable { iterator(): AsyncIterator; - apply(pipeline: (enumerable: this) => TResult): TResult; + apply(pipeline: (enumerable: AsyncEnumerable) => TResult): TResult; count(predicate?: MaybeAsyncPredicate): Promise; nonEnumeratedCount(): Promise; fastCount(): Promise; + maxCount(): Promise; select(selector: MaybeAsyncConverter): AsyncEnumerable; selectMany(selector: MaybeAsyncConverter>): AsyncEnumerable; @@ -55,7 +56,7 @@ export interface AsyncEnumerable extends AsyncIterable { contains(obj: TElement, equater?: MaybeAsyncEquater): Promise; - sequenceEqual(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): Promise; + sequenceEquals(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater): Promise; append(obj: TElement): AsyncEnumerable; @@ -108,6 +109,8 @@ export interface AsyncEnumerable extends AsyncIterable { all(predicate: MaybeAsyncPredicate): Promise; any(predicate: MaybeAsyncPredicate): Promise; any(): Promise; + none(predicate: Predicate): Promise; + none(): Promise; skip(n: number): AsyncEnumerable; skipLast(n: number): AsyncEnumerable; @@ -123,18 +126,21 @@ export interface AsyncEnumerable extends AsyncIterable { zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]>; - index(): AsyncEnumerable<[number, TElement]>; + indexex(): AsyncEnumerable<[number, TElement]>; - reverse(): AsyncEnumerable; + reversed(): AsyncEnumerable; - chunk(size: number): AsyncEnumerable; + chunked(size: number): AsyncEnumerable; - cache(): AsyncEnumerable; + // random(options?: RandomOptions): TElement | undefined; + cached(): AsyncEnumerable; + + asArray(): Promise; toArray(): Promise; toMap(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; toSet(): Promise>; - toObject(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; + toObject(keySelector: MaybeAsyncConverter, valueSelector: MaybeAsyncConverter): Promise>; collect(collector: Collector): Promise; } @@ -159,116 +165,100 @@ export interface OrderedAsyncEnumerable extends AsyncEnumerable(enumerable: Enumerable): AsyncEnumerable { - return new WrappedEnumerable(enumerable); -} - -export function wrap(iterable: MaybeAsyncIterable): AsyncEnumerable { - if (iterable instanceof BaseAsyncEnumerable) { - return iterable; +export namespace AsyncEnumerable { + export function asAsync(enumerable: Enumerable): AsyncEnumerable { + return new WrappedEnumerable(enumerable); } - if (isAsyncIterable(iterable)) { - return sequence(iterable); + export function wrap(iterable: MaybeAsyncIterable): AsyncEnumerable { + if (iterable instanceof BaseAsyncEnumerable) { + return iterable; + } + + if (isAsyncIterable(iterable)) { + return sequence(iterable); + } + + return asAsync(Enumerable.wrap(iterable)); } - return asAsync(wrapSync(iterable)); -} + export function sequence(iterable: AsyncIterable): AsyncEnumerable { + return new WrappedAsyncIterable(iterable); + } -export function sequence(iterable: AsyncIterable): AsyncEnumerable { - return new WrappedAsyncIterable(iterable); -} + export function empty(): AsyncEnumerable { + return EmptyAsyncEnumerable.INSTANCE; + } -export function empty(): AsyncEnumerable { - return EmptyAsyncEnumerable.INSTANCE; -} + export function single(obj: T): AsyncEnumerable { + return new WrappedObjectAsync(obj); + } -export function single(obj: T): AsyncEnumerable { - return new WrappedObjectAsync(obj); -} + export function array(array: T[]): AsyncEnumerable { + return new WrappedArrayAsync(array); + } -export function array(array: T[]): AsyncEnumerable { - return new WrappedArrayAsync(array); -} + export function arrayLike(arrayLike: ArrayLike): AsyncEnumerable { + return new WrappedArrayLikeAsync(arrayLike); + } -export function arrayLike(arrayLike: ArrayLike): AsyncEnumerable { - return new WrappedArrayLikeAsync(arrayLike); -} + export function of(...elements: T[]): AsyncEnumerable { + switch (elements.length) { + case 0: + return empty(); + case 1: + return single(elements[0]); + default: + return array(elements); + } + } -export function of(...elements: T[]): AsyncEnumerable { - switch (elements.length) { - case 0: + 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 RangeError(); + } + + if (count === 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 RangeError(); - } - - if (count === 0) { - return empty(); - } - - if (count === 1) { - return new WrappedObjectAsync(value); - } - - return new RepeatAsyncEnumerable(value, count); -} - -export namespace sum { - export async function number(iterable: AsyncIterable) { - let result = 0; - - for await (const n of iterable) { - result += n; } - return result; - } - - export async function bigint(iterable: AsyncIterable) { - let result = 0n; - - for await (const n of iterable) { - result += n; + if (count === 1) { + return new WrappedObjectAsync(value); } - return result; + return new RepeatAsyncEnumerable(value, count); + } + + export function isAsyncEnumerable(obj: any): obj is Enumerable { + return obj instanceof AsyncEnumerableMarker; } } @@ -276,14 +266,18 @@ export namespace sum { //#region implementations -export abstract class BaseAsyncEnumerable implements AsyncEnumerable { +class AsyncEnumerableMarker { + +} + +export abstract class BaseAsyncEnumerable extends AsyncEnumerableMarker implements AsyncEnumerable { [Symbol.asyncIterator]() { return this.iterator(); } abstract iterator(): AsyncIterator; - apply(pipeline: (enumerable: this) => TResult) { + apply(pipeline: (enumerable: AsyncEnumerable) => TResult): TResult { return pipeline(this); } @@ -304,11 +298,11 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater): AsyncEnumerable { - return new JoinAsyncEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new JoinAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater): AsyncEnumerable { - return new GroupJoinAsyncEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new GroupJoinAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } async contains(obj: TElement, equater?: MaybeAsyncEquater) { @@ -325,12 +319,12 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable, equater?: MaybeAsyncEquater) { + async sequenceEquals(iterable: MaybeAsyncIterable, equater?: MaybeAsyncEquater) { if (this === iterable) { return true; } - const that = wrap(iterable); + const that = AsyncEnumerable.wrap(iterable); const thisCount = await this.nonEnumeratedCount(); const thatCount = await that.nonEnumeratedCount(); @@ -351,7 +345,7 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable implements AsyncEnumerable[] = [this]; for (const iterable of iterables) { - arr.push(wrap(iterable)); + arr.push(AsyncEnumerable.wrap(iterable)); } return new ConcatAsyncEnumerable(arr); @@ -419,6 +413,11 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable= 0 ? n : await this.count(); } + async maxCount() { + const n = await this.nonEnumeratedCount(); + return n >= 0 ? n : Infinity; + } + async #tryGetFirst(predicate?: MaybeAsyncPredicate) { if (predicate) { for await (const element of this) { @@ -723,27 +722,27 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable, equater?: MaybeAsyncEquater): AsyncEnumerable { - return new UnionAsyncEnumerable(this, wrap(iterable), equater); + return new UnionAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), equater); } unionBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncEnumerable { - return new UnionByAsyncEnumerable(this, wrap(iterable), selector, equater); + return new UnionByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector, equater); } except(iterable: MaybeAsyncIterable): AsyncEnumerable { - return new ExceptAsyncEnumerable(this, wrap(iterable)); + return new ExceptAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); } exceptBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncEnumerable { - return new ExceptByAsyncEnumerable(this, wrap(iterable), selector); + return new ExceptByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector); } intersect(iterable: MaybeAsyncIterable): AsyncEnumerable { - return new IntersectAsyncEnumerable(this, wrap(iterable)); + return new IntersectAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); } intersectBy(iterable: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncEnumerable { - return new IntersectByAsyncEnumerable(this, wrap(iterable), selector); + return new IntersectByAsyncEnumerable(this, AsyncEnumerable.wrap(iterable), selector); } async all(predicate: MaybeAsyncPredicate) { @@ -782,6 +781,26 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable 0; } + async none(predicate?: MaybeAsyncPredicate) { + const n = await this.nonEnumeratedCount(); + + if (n === 0) { + return true; + } + + if (predicate) { + for await (const element of this) { + if (predicate(element)) { + return false; + } + } + + return true; + } + + return n < 0 && (await this.iterator().next()).done === true; + } + skip(n: number): AsyncEnumerable { if (n < 0) { throw new Error("Cannot skip a negative number of elements."); @@ -807,7 +826,7 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable() : new TakeAsyncEnumerable(this, n); + return n === 0 ? AsyncEnumerable.empty() : new TakeAsyncEnumerable(this, n); } takeLast(n: number): AsyncEnumerable { @@ -815,7 +834,7 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable() : new TakeLastAsyncEnumerable(this, n); + return n === 0 ? AsyncEnumerable.empty() : new TakeLastAsyncEnumerable(this, n); } takeWhile(predicate: MaybeAsyncPredicate): AsyncEnumerable { @@ -833,18 +852,18 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]> { - return new ZippedAsyncEnumerable(this, wrap(iterable)); + return new ZippedAsyncEnumerable(this, AsyncEnumerable.wrap(iterable)); } - index(): AsyncEnumerable<[number, TElement]> { + indexex(): AsyncEnumerable<[number, TElement]> { return new IndexedAsyncEnumerable(this); } - reverse(): AsyncEnumerable { + reversed(): AsyncEnumerable { return new ReversedAsyncEnumerable(this); } - chunk(size: number): AsyncEnumerable { + chunked(size: number): AsyncEnumerable { if (size <= 0) { throw new Error("Chunk size must be positive."); } @@ -852,10 +871,14 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable(this, size); } - cache(): AsyncEnumerable { + cached(): AsyncEnumerable { return new CacheAsyncEnumerable(this); } + async asArray() { + return await this.toArray(); + } + async toArray() { const array: TElement[] = []; @@ -913,28 +936,313 @@ export abstract class BaseAsyncEnumerable implements AsyncEnumerable extends BaseAsyncEnumerable implements GroupedAsyncEnumerable { - readonly #key: TKey; - readonly #grouping: AsyncEnumerable; +class DelegatedAsyncEnumerable extends AsyncEnumerableMarker implements AsyncEnumerable { + #enumerable: AsyncEnumerable; - constructor(key: TKey, grouping: AsyncEnumerable) { + constructor(enumerable: AsyncEnumerable) { super(); + this.#enumerable = enumerable; + } + + get enumerable() { + return this.#enumerable; + } + + protected set enumerable(value: AsyncEnumerable) { + this.#enumerable = value; + } + + [Symbol.asyncIterator]() { + return this.iterator(); + } + + iterator() { + return this.#enumerable.iterator(); + } + + apply(pipeline: (enumerable: AsyncEnumerable) => TResult) { + return this.#enumerable.apply(pipeline); + } + + count(predicate?: ((obj: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.count(predicate); + } + + nonEnumeratedCount(): Promise { + return this.#enumerable.nonEnumeratedCount(); + } + + fastCount(): Promise { + return this.#enumerable.fastCount(); + } + + maxCount(): Promise { + return this.#enumerable.maxCount(); + } + + select(selector: (obj: TElement) => MaybePromise): AsyncEnumerable { + return this.#enumerable.select(selector); + } + + selectMany(selector: (obj: TElement) => MaybePromise>): AsyncEnumerable { + return this.#enumerable.selectMany(selector); + } + + where(predicate: (obj: TElement) => MaybePromise): AsyncEnumerable { + return this.#enumerable.where(predicate); + } + + groupBy(keySelector: (obj: TElement) => MaybePromise, elementSelector?: undefined, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable>; + groupBy(keySelector: (obj: TElement) => MaybePromise, elementSelector: (obj: TElement) => MaybePromise, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable>; + groupBy(keySelector: any, elementSelector?: any, keyComparer?: any) { + 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: (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); + } + + groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: (obj: TElement) => MaybePromise, secondKeySelector: (obj: TOther) => MaybePromise, resultSelector?: undefined, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable>; + groupJoin(iterable: MaybeAsyncIterable, firstKeySelector: (obj: TElement) => MaybePromise, secondKeySelector: (obj: TOther) => MaybePromise, resultSelector: (first: TElement, second: AsyncEnumerable) => MaybePromise, keyComparer?: ((first: TKey, second: TKey) => MaybePromise) | undefined): AsyncEnumerable; + groupJoin(iterable: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { + return this.#enumerable.groupJoin(iterable, firstKeySelector, secondKeySelector, resultSelector, keyComparer); + } + + contains(obj: TElement, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.contains(obj, equater); + } + + sequenceEquals(iterable: MaybeAsyncIterable, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.sequenceEquals(iterable, equater); + } + + append(obj: TElement): AsyncEnumerable { + return this.#enumerable.append(obj); + } + + prepend(obj: TElement): AsyncEnumerable { + return this.#enumerable.prepend(obj); + } + + remove(obj: TElement, all?: boolean | undefined, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.remove(obj, all, equater); + } + + concat(...iterables: MaybeAsyncIterable[]): AsyncEnumerable { + return this.#enumerable.concat(...iterables); + } + + first(predicate?: ((obj: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.first(predicate); + } + + firstOrDefault(predicate?: ((obj: TElement) => MaybePromise) | undefined, def?: TElement | undefined): Promise { + return this.#enumerable.firstOrDefault(predicate, def); + } + + last(predicate?: ((obj: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.last(predicate); + } + + lastOrDefault(predicate?: ((obj: TElement) => MaybePromise) | undefined, def?: TElement | undefined): Promise { + return this.#enumerable.lastOrDefault(predicate, def); + } + + single(predicate?: ((obj: TElement) => MaybePromise) | undefined): Promise { + return this.#enumerable.single(predicate); + } + + singleOrDefault(predicate?: ((obj: TElement) => MaybePromise) | undefined, def?: TElement | undefined): Promise { + return this.#enumerable.singleOrDefault(predicate, def); + } + + elementAt(index: number): Promise { + return this.#enumerable.elementAt(index); + } + + elementAtOrDefault(index: number, def?: TElement | undefined): Promise { + return this.#enumerable.elementAtOrDefault(index, def); + } + + aggregate(accumulator: (acc: TElement, obj: TElement) => MaybePromise): Promise; + aggregate(accumulator: (acc: TAccumulator, obj: TElement) => MaybePromise, seed?: TAccumulator | undefined): Promise; + aggregate(accumulator: (acc: TAccumulator, obj: TElement) => MaybePromise, seed?: TAccumulator | undefined, resultSelector?: ((obj: TAccumulator) => MaybePromise) | undefined): Promise; + aggregate(accumulator: any, seed?: any, resultSelector?: any) { + return this.#enumerable.aggregate(accumulator, seed, resultSelector); + } + + min(): Promise { + return this.#enumerable.min(); + } + + minBy(selector: (obj: TElement) => MaybePromise): Promise { + return this.#enumerable.minBy(selector); + } + + max(): Promise { + return this.#enumerable.max(); + } + + maxBy(selector: (obj: TElement) => MaybePromise): Promise { + return this.#enumerable.maxBy(selector); + } + + order(comparer?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.order(comparer); + } + + orderBy(selector: (obj: TElement) => MaybePromise, comparer?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.orderBy(selector, comparer); + } + + orderDescending(comparer?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.orderDescending(comparer); + } + + orderByDescending(selector: (obj: TElement) => MaybePromise, comparer?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.orderByDescending(selector, comparer); + } + + distinct(equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.distinct(equater); + } + + distinctBy(selector: (obj: TElement) => MaybePromise, equater?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.distinctBy(selector, equater); + } + + union(iterable: MaybeAsyncIterable, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.union(iterable, equater); + } + + unionBy(iterable: MaybeAsyncIterable, selector: (obj: TElement) => MaybePromise, equater?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.unionBy(iterable, selector, equater); + } + + except(iterable: MaybeAsyncIterable, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.except(iterable, equater); + } + + exceptBy(iterable: MaybeAsyncIterable, selector: (obj: TElement) => MaybePromise, equater?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.exceptBy(iterable, selector, equater); + } + + intersect(iterable: MaybeAsyncIterable, equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.intersect(iterable, equater); + } + + intersectBy(iterable: MaybeAsyncIterable, selector: (obj: TElement) => MaybePromise, equater?: ((first: TBy, second: TBy) => MaybePromise) | undefined): AsyncEnumerable { + return this.#enumerable.intersectBy(iterable, selector, equater); + } + + all(predicate: (obj: TElement) => MaybePromise): Promise { + return this.#enumerable.all(predicate); + } + + any(predicate: (obj: TElement) => MaybePromise): Promise; + any(): Promise; + any(predicate?: any) { + return this.#enumerable.any(predicate); + } + + none(predicate: Predicate): Promise; + none(): Promise; + none(predicate?: any) { + return this.#enumerable.none(predicate); + } + + skip(n: number): AsyncEnumerable { + return this.#enumerable.skip(n); + } + + skipLast(n: number): AsyncEnumerable { + return this.#enumerable.skipLast(n); + } + + skipWhile(condition: (obj: TElement) => MaybePromise): AsyncEnumerable { + return this.#enumerable.skipWhile(condition); + } + + take(n: number): AsyncEnumerable { + return this.#enumerable.take(n); + } + + takeLast(n: number): AsyncEnumerable { + return this.#enumerable.takeLast(n); + } + + takeWhile(condition: (obj: TElement) => MaybePromise): AsyncEnumerable { + return this.#enumerable.takeWhile(condition); + } + + peek(action: (obj: TElement) => MaybePromise): AsyncEnumerable { + return this.#enumerable.peek(action); + } + + forEach(action: (obj: TElement) => MaybePromise): Promise { + return this.#enumerable.forEach(action); + } + + zip(iterable: MaybeAsyncIterable): AsyncEnumerable<[TElement, TOther]> { + return this.#enumerable.zip(iterable); + } + + indexex(): AsyncEnumerable<[number, TElement]> { + return this.#enumerable.indexex(); + } + + reversed(): AsyncEnumerable { + return this.#enumerable.reversed(); + } + + chunked(size: number): AsyncEnumerable { + return this.#enumerable.chunked(size); + } + + cached(): AsyncEnumerable { + return this.#enumerable.cached(); + } + + asArray(): Promise { + return this.#enumerable.asArray(); + } + + toArray(): Promise { + return this.#enumerable.toArray(); + } + + toMap(keySelector: (obj: TElement) => MaybePromise, valueSelector: (obj: TElement) => MaybePromise): Promise> { + return this.#enumerable.toMap(keySelector, valueSelector); + } + + toSet(): Promise> { + return this.#enumerable.toSet(); + } + + toObject(keySelector: (obj: TElement) => MaybePromise, valueSelector: (obj: TElement) => MaybePromise): Promise> { + return this.#enumerable.toObject(keySelector, valueSelector); + } + + collect(collector: Collector): Promise { + return this.#enumerable.collect(collector); + } +} + +class GroupedAsyncEnumerableImpl extends DelegatedAsyncEnumerable implements GroupedAsyncEnumerable { + readonly #key: TKey; + + constructor(key: TKey, grouping: AsyncEnumerable) { + super(grouping); + this.#key = key; - this.#grouping = grouping; } public get key() { return this.#key; } - - override async nonEnumeratedCount() { - return await this.#grouping.nonEnumeratedCount(); - } - - override iterator() { - return this.#grouping.iterator(); - } } abstract class BaseOrderedAsyncEnumerable extends BaseAsyncEnumerable implements OrderedAsyncEnumerable { @@ -954,6 +1262,10 @@ abstract class BaseOrderedAsyncEnumerable extends BaseAsyncEnumerable< return await this.#enumerable.nonEnumeratedCount(); } + override async maxCount() { + return await this.#enumerable.maxCount(); + } + get comparer() { return this.#sorter; } @@ -1922,7 +2234,7 @@ class GroupByAsyncEnumerable extends BaseAsyncEnumerabl } for await (const entry of groupings) { - yield new GroupedAsyncEnumerableImpl(entry[0], array(entry[1])); + yield new GroupedAsyncEnumerableImpl(entry[0], AsyncEnumerable.array(entry[1])); } } } @@ -2032,7 +2344,7 @@ class GroupJoinAsyncEnumerable extends BaseAsyncE } // yield this.#resultSelector(firstObj, this.#second.where(secondObj => this.#keyComparer(firstKey, this.#secondKeySelector(secondObj)))); - yield this.#resultSelector(firstObj, array(secondObjs)); + yield this.#resultSelector(firstObj, AsyncEnumerable.array(secondObjs)); } } } diff --git a/src/bitarray.ts b/src/bitarray.ts index 5819004..495877c 100644 --- a/src/bitarray.ts +++ b/src/bitarray.ts @@ -1,5 +1,5 @@ import { join } from "./collector.js"; -import { arrayLike, sequence } from "./sync.js"; +import { Enumerable } from "./sync.js"; import { asArray, isDefined } from "./utils.js"; export interface BitArray extends Iterable { @@ -224,16 +224,16 @@ class BitArrayImpl implements BitArray { this.#ensureSameSize(other); return other instanceof BitArrayImpl ? - 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); + 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); } public intersects(other: BitArray) { this.#ensureSameSize(other); return other instanceof BitArrayImpl ? - arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0) : - sequence(this).zip(sequence(other)).any(([a, b]) => a && b); + 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); } public slice(offset: number, length: number) { @@ -247,15 +247,15 @@ class BitArrayImpl implements BitArray { } public toArray() { - return sequence(this).toArray(); + return Enumerable.sequence(this).toArray(); } public equals(other: BitArray) { - return other === this || isDefined(other) && (other instanceof BitArrayImpl ? arrayLike(this.#bits).equals(arrayLike(other.#bits)) : sequence(this).equals(sequence(other))); + return other === this || isDefined(other) && (other instanceof BitArrayImpl ? Enumerable.arrayLike(this.#bits).sequenceEquals(Enumerable.arrayLike(other.#bits)) : Enumerable.sequence(this).sequenceEquals(Enumerable.sequence(other))); } public toString() { - return sequence(this).select(bit => bit ? '1' : '0').collect(join()); + return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(join()); } } @@ -465,13 +465,13 @@ class BitArraySlice implements BitArray { public contains(other: BitArray) { this.#ensureSameSize(other); - return sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); + return Enumerable.sequence(this).zip(Enumerable.sequence(other)).where(([, b]) => b).all(([a, b]) => a && b); } public intersects(other: BitArray) { this.#ensureSameSize(other); - return sequence(this).zip(sequence(other)).any(([a, b]) => a && b); + return Enumerable.sequence(this).zip(Enumerable.sequence(other)).any(([a, b]) => a && b); } public slice(offset: number, length: number) { @@ -489,15 +489,15 @@ class BitArraySlice implements BitArray { } public toArray() { - return sequence(this).toArray(); + return Enumerable.sequence(this).toArray(); } public equals(other: BitArray) { - return other === this || isDefined(other) && sequence(this).equals(sequence(other)); + return other === this || isDefined(other) && Enumerable.sequence(this).sequenceEquals(Enumerable.sequence(other)); } public toString() { - return sequence(this).select(bit => bit ? '1' : '0').collect(join()); + return Enumerable.sequence(this).select(bit => bit ? '1' : '0').collect(join()); } } diff --git a/src/index.ts b/src/index.ts index f05852a..206eeda 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ export * from "./sync.js"; -export * as async from "./async.js"; +export * from "./async.js"; export * as collectors from "./collector.js"; -export * as random from "./random.js"; \ No newline at end of file +export * as random from "./random.js"; diff --git a/src/sync.ts b/src/sync.ts index 861c966..c136900 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -41,7 +41,7 @@ export interface Enumerable extends Iterable { contains(obj: TElement, equater?: Equater): boolean; - equals(iterable: Iterable, equater?: Equater): boolean; + sequenceEquals(iterable: Iterable, equater?: Equater): boolean; append(obj: TElement): Enumerable; @@ -146,147 +146,149 @@ export interface OrderedEnumerable extends Enumerable { thenByDescending(selector: Converter, comparer?: Comparer): OrderedEnumerable; } -export function wrap(iterable: Iterable): Enumerable { - if (isEnumerable(iterable)) { - return iterable; +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); } - if (Array.isArray(iterable)) { - return array(iterable); + export function sequence(iterable: Iterable): Enumerable { + return new WrappedIterable(iterable); } - if (iterable instanceof Set) { - return set(iterable); + export function empty(): Enumerable { + return EmptyEnumerable.INSTANCE; } - if (iterable instanceof Map) { - return map(iterable) as unknown as Enumerable; + export function single(obj: T): Enumerable { + return new WrappedObject(obj); } - return sequence(iterable); -} + export function array(array: T[]): Enumerable { + return new WrappedArray(array); + } -export function sequence(iterable: Iterable): Enumerable { - return new WrappedIterable(iterable); -} + export function arrayLike(arrayLike: ArrayLike): Enumerable { + return new WrappedArrayLike(arrayLike); + } -export function empty(): Enumerable { - return EmptyEnumerable.INSTANCE; -} + export function set(set: Set): Enumerable { + return new WrappedSet(set); + } -export function single(obj: T): Enumerable { - return new WrappedObject(obj); -} + export function map(map: Map): Enumerable<[K, V]> { + return new WrappedMap(map); + } -export function array(array: T[]): Enumerable { - return new WrappedArray(array); -} + 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 arrayLike(arrayLike: ArrayLike): Enumerable { - return new WrappedArrayLike(arrayLike); -} + export function ofPropertyKeys(...elements: T[]): Enumerable { + return of(...elements); + } -export function set(set: Set): Enumerable { - return new WrappedSet(set); -} + export function entries(o: Record | ArrayLike): Enumerable<[string, T]> { + return array(Object.entries(o)); + } -export function map(map: Map): Enumerable<[K, V]> { - return new WrappedMap(map); -} + export function keys(o: object): Enumerable { + return array(Object.keys(o)); + } -export function of(...elements: T[]): Enumerable { - switch (elements.length) { - case 0: + 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(); - case 1: - return single(elements[0]); - default: - return array(elements); - } -} + } -export function ofPropertyKeys(...elements: T[]): Enumerable { - return of(...elements); -} + if (count === 1) { + return new WrappedObject(value); + } -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; + return new RepeatEnumerable(value, count); } - if (c === undefined) { - c = 1; + export function randomSequence(random?: RandomGenerator): Enumerable { + return new FunctionEnumerable(random ?? mathRandom); } - 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; + export function concat(...enumerables: Enumerable[]): Enumerable { + return new ConcatEnumerable(enumerables); } - if (c === undefined) { - c = 1n; + export function isEnumerable(obj: any): obj is Enumerable { + return obj instanceof EnumerableMarker; } - - 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; } //#region enumerable implementation @@ -323,11 +325,11 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } join(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: Equater): Enumerable { - return new JoinEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new JoinEnumerable(this, Enumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(iterable: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: Equater): Enumerable { - return new GroupJoinEnumerable(this, wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); + return new GroupJoinEnumerable(this, Enumerable.wrap(iterable), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } contains(obj: TElement, equater?: Equater) { @@ -344,8 +346,12 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen return false; } - equals(iterable: Iterable, equater?: Equater) { - const that = wrap(iterable); + sequenceEquals(iterable: Iterable, equater?: Equater) { + if (this === iterable) { + return true; + } + + const that = Enumerable.wrap(iterable); const thisCount = this.nonEnumeratedCount(); const thatCount = that.nonEnumeratedCount(); @@ -399,7 +405,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen const arr: Enumerable[] = [this]; for (const iterable of iterables) { - arr.push(wrap(iterable)); + arr.push(Enumerable.wrap(iterable)); } return new ConcatEnumerable(arr); @@ -743,27 +749,27 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } union(iterable: Iterable, equater?: Equater): Enumerable { - return new UnionEnumerable(this, wrap(iterable), equater); + return new UnionEnumerable(this, Enumerable.wrap(iterable), equater); } unionBy(iterable: Iterable, selector: Converter, equater?: Equater): Enumerable { - return new UnionByEnumerable(this, wrap(iterable), selector, equater); + return new UnionByEnumerable(this, Enumerable.wrap(iterable), selector, equater); } except(iterable: Iterable): Enumerable { - return new ExceptEnumerable(this, wrap(iterable)); + return new ExceptEnumerable(this, Enumerable.wrap(iterable)); } exceptBy(iterable: Iterable, selector: Converter): Enumerable { - return new ExceptByEnumerable(this, wrap(iterable), selector); + return new ExceptByEnumerable(this, Enumerable.wrap(iterable), selector); } intersect(iterable: Iterable): Enumerable { - return new IntersectEnumerable(this, wrap(iterable)); + return new IntersectEnumerable(this, Enumerable.wrap(iterable)); } intersectBy(iterable: Iterable, selector: Converter): Enumerable { - return new IntersectByEnumerable(this, wrap(iterable), selector); + return new IntersectByEnumerable(this, Enumerable.wrap(iterable), selector); } all(predicate: Predicate) { @@ -847,7 +853,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? empty() : new TakeEnumerable(this, n); + return n === 0 ? Enumerable.empty() : new TakeEnumerable(this, n); } takeLast(n: number): Enumerable { @@ -855,7 +861,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen throw new Error("Cannot take a negative number of elements."); } - return n === 0 ? empty() : new TakeLastEnumerable(this, n); + return n === 0 ? Enumerable.empty() : new TakeLastEnumerable(this, n); } takeWhile(predicate: Predicate): Enumerable { @@ -873,7 +879,7 @@ export abstract class BaseEnumerable extends EnumerableMarker implemen } zip(iterable: Iterable): Enumerable<[TElement, TOther]> { - return new ZippedEnumerable(this, wrap(iterable)); + return new ZippedEnumerable(this, Enumerable.wrap(iterable)); } indexed(): Enumerable<[number, TElement]> { @@ -1025,8 +1031,8 @@ class DelegatedEnumerable extends EnumerableMarker implements Enumerab return this.#enumerable.contains(obj, equater); } - equals(iterable: Iterable, equater?: Equater) { - return this.#enumerable.equals(iterable, equater); + sequenceEquals(iterable: Iterable, equater?: Equater) { + return this.#enumerable.sequenceEquals(iterable, equater); } append(obj: TElement) { @@ -2495,7 +2501,7 @@ class GroupByEnumerable extends BaseEnumerable extends BaseEnumerable< } } - yield this.#resultSelector(firstObj, array(secondObjs)); + yield this.#resultSelector(firstObj, Enumerable.array(secondObjs)); } } } @@ -2677,7 +2683,7 @@ class CacheEnumerable extends DelegatedEnumerable { override iterator() { if (!this.#cached) { - this.enumerable = array(this.enumerable.toArray()); + this.enumerable = Enumerable.array(this.enumerable.toArray()); this.#cached = true; }