diff --git a/src/async/impl.ts b/src/async/impl.ts index 1e789c1..9e64279 100644 --- a/src/async/impl.ts +++ b/src/async/impl.ts @@ -456,6 +456,14 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return new OrderByAsyncSequence(this, true, selector, comparer); } + partition(equater?: MaybeAsyncEquater): AsyncSequence[]> { + return new PartitionAsyncSequence(this, equater); + } + + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence[]> { + return new PartitionByAsyncSequence(this, selector, equater); + } + distinct(equater?: MaybeAsyncEquater): AsyncSequence { return new DistinctAsyncSequence(this, equater); } @@ -849,6 +857,14 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.orderByDescending(selector, comparer); } + partition(equater?: MaybeAsyncEquater | undefined): AsyncSequence[]> { + return this.#sequence.partition(equater); + } + + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence[]> { + return this.#sequence.partitionBy(selector, equater); + } + distinct(equater?: ((first: TElement, second: TElement) => MaybePromise) | undefined): AsyncSequence { return this.#sequence.distinct(equater); } @@ -2141,3 +2157,62 @@ export class CacheAsyncSequence extends BaseAsyncSequence { yield* this.#cache ??= await this.#sequence.toArray(); } } + +class PartitionAsyncSequence extends BaseAsyncSequence[]> { + readonly #sequence: AsyncSequence; + readonly #equater: MaybeAsyncEquater; + + constructor(sequence: AsyncSequence, equater: MaybeAsyncEquater | undefined) { + super(); + + this.#sequence = sequence; + this.#equater = equater ?? strictEquals; + } + + override async *iterator() { + const partitions = createAsyncEqualityMap[]>(this.#equater); + + for await (const obj of this.#sequence) { + const partition = await partitions.get(obj); + + if (partition) { + partition.push(obj); + } else { + await partitions.set(obj, [obj]); + } + } + + yield* partitions.values(); + } +} + +class PartitionByAsyncSequence extends BaseAsyncSequence[]> { + readonly #sequence: AsyncSequence; + readonly #selector: MaybeAsyncConverter; + readonly #equater: MaybeAsyncEquater; + + constructor(sequence: AsyncSequence, selector: MaybeAsyncConverter, equater: MaybeAsyncEquater | undefined) { + super(); + + this.#sequence = sequence; + this.#selector = selector; + this.#equater = equater ?? strictEquals; + } + + override async *iterator() { + const partitions = createAsyncEqualityMap[]>(this.#equater); + + for await (const obj of this.#sequence) { + const key = await this.#selector(obj); + const partition = await partitions.get(key); + + if (partition) { + partition.push(obj); + } else { + await partitions.set(key, [obj]); + } + } + + yield* partitions.values(); + } +} diff --git a/src/async/types.ts b/src/async/types.ts index 2a0723d..4a729b3 100644 --- a/src/async/types.ts +++ b/src/async/types.ts @@ -66,6 +66,9 @@ export interface AsyncSequence extends AsyncIterable orderDescending(comparer?: MaybeAsyncComparer): AsyncSequence; orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncSequence; + partition(equater?: MaybeAsyncEquater): AsyncSequence[]>; + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence[]>; + distinct(equater?: MaybeAsyncEquater): AsyncSequence; distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence; diff --git a/src/sync/impl.ts b/src/sync/impl.ts index cacd1ab..2bddb09 100644 --- a/src/sync/impl.ts +++ b/src/sync/impl.ts @@ -454,6 +454,14 @@ export abstract class BaseSequence extends SequenceMarker implements S return new OrderBySequence(this, true, selector, comparer); } + partition(equater?: Equater): Sequence { + return new PartitionSequence(this, equater); + } + + partitionBy(selector: Converter, equater?: Equater): Sequence { + return new PartitionBySequence(this, selector, equater); + } + distinct(equater?: Equater): Sequence { return new DistinctSequence(this, equater); } @@ -833,6 +841,14 @@ export class DelegatedSequence extends SequenceMarker implements Seque return this.#sequence.orderByDescending(selector, comparer); } + partition(equater?: Equater | undefined): Sequence { + return this.#sequence.partition(equater); + } + + partitionBy(selector: Converter, equater?: Equater | undefined): Sequence { + return this.#sequence.partitionBy(selector, equater); + } + distinct(equater?: Equater) { return this.#sequence.distinct(equater); } @@ -2404,3 +2420,62 @@ export class CacheSequence extends DelegatedSequence { return super.iterator(); } } + +class PartitionSequence extends BaseSequence { + readonly #sequence: Sequence; + readonly #equater: Equater; + + constructor(sequence: Sequence, equater: Equater | undefined) { + super(); + + this.#sequence = sequence; + this.#equater = equater ?? strictEquals; + } + + override *iterator() { + const partitions = createEqualityMap(this.#equater); + + for (const obj of this.#sequence) { + const partition = partitions.get(obj); + + if (partition) { + partition.push(obj); + } else { + partitions.set(obj, [obj]); + } + } + + yield* partitions.values(); + } +} + +class PartitionBySequence extends BaseSequence { + readonly #sequence: Sequence; + readonly #selector: Converter; + readonly #equater: Equater; + + constructor(sequence: Sequence, selector: Converter, equater: Equater | undefined) { + super(); + + this.#sequence = sequence; + this.#selector = selector; + this.#equater = equater ?? strictEquals; + } + + override *iterator() { + const partitions = createEqualityMap(this.#equater); + + for (const obj of this.#sequence) { + const key = this.#selector(obj); + const partition = partitions.get(key); + + if (partition) { + partition.push(obj); + } else { + partitions.set(key, [obj]); + } + } + + yield* partitions.values(); + } +} diff --git a/src/sync/types.ts b/src/sync/types.ts index d09dcac..98f58bf 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -67,6 +67,9 @@ export interface Sequence extends Iterable { orderDescending(comparer?: Comparer): OrderedSequence; orderByDescending(selector: Converter, comparer?: Comparer): OrderedSequence; + partition(equater?: Equater): Sequence; + partitionBy(selector: Converter, equater?: Equater): Sequence; + distinct(equater?: Equater): Sequence; distinctBy(selector: Converter, equater?: Equater): Sequence;