import { BaseAsyncSequence } from "../async/impl.js"; import { AsyncSequence } from "../async/types.js"; 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 } from "./index.js"; import { Sequence, GroupedSequence, OrderedSequence } from "./types.js"; export class SequenceMarker { } export abstract class BaseSequence extends SequenceMarker implements Sequence { [Symbol.iterator]() { return this.iterator(); } abstract iterator(): Iterator; apply(pipeline: (sequence: this) => TResult) { return pipeline(this); } select(converter: Converter): Sequence { return new SelectSequence(this, converter); } selectMany(converter: Converter>): Sequence { return new SelectManySequence(this, converter); } where(predicate: FilterPredicate): Sequence { return new WhereSequence(this, predicate); } groupBy(keySelector: Converter, elementSelector?: Converter, keyComparer?: Equater): Sequence> { return new GroupBySequence(this, keySelector, elementSelector, keyComparer); } join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: Equater): Sequence { return new JoinSequence(this, sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: Equater): Sequence { return new GroupJoinSequence(this, sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } contains(obj: TElement, equater?: Equater) { if (!equater) { equater = strictEquals; } for (const element of this) { if (equater(element, obj)) { return true; } } return false; } sequenceEquals(sequence: Sequence, equater?: Equater) { if (this === sequence) { return true; } const thisCount = this.nonEnumeratedCount(); const thatCount = sequence.nonEnumeratedCount(); if (thisCount >= 0 && thatCount >= 0 && thisCount !== thatCount) { return false; } if (!equater) { equater = strictEquals; } const thisIterator = this.iterator(); const thatIterator = sequence.iterator(); while (true) { const thisNext = thisIterator.next(); const thatNext = thatIterator.next(); if (thisNext.done) { return thatNext.done === true; } if (thatNext.done) { return false; } if (!equater(thisNext.value, thatNext.value)) { return false; } } } append(obj: TElement): Sequence { return new AppendSequence(this, obj); } prepend(obj: TElement): Sequence { return new PrependSequence(this, obj); } remove(obj: TElement, all?: boolean, equater?: Equater): Sequence { return new RemoveSequence(this, obj, all, equater); } concat(...sequences: Sequence[]) { if (sequences.length === 0) { return this; } const arr: Sequence[] = [this]; for (const sequence of sequences) { arr.push(sequence); } return new ConcatSequence(arr); } count(predicate?: Predicate) { let count = 0; if (predicate) { for (const element of this) { if (predicate(element)) { count++; } } } else { const iterator = this.iterator(); while (!iterator.next().done) { count++; } } return count; } nonEnumeratedCount() { return -1; } fastCount() { const n = this.nonEnumeratedCount(); return n >= 0 ? n : this.count(); } maxCount() { const n = this.nonEnumeratedCount(); return n >= 0 ? n : Infinity; } #tryGetFirst(predicate?: Predicate): { found: boolean, element?: TElement | undefined } { if (predicate) { for (const element of this) { if (predicate(element)) { return { found: true, element }; } } } else { const next = this.iterator().next(); if (!next.done) { return { found: true, element: next.value }; } } return { found: false }; } first(predicate?: Predicate) { const result = this.#tryGetFirst(predicate); if (result.found) { return result.element!; } throw new Error("No element was found."); } firstOrDefault(predicate?: Predicate, def?: TElement) { const result = this.#tryGetFirst(predicate); return result.found ? result.element : def; } #tryGetLast(predicate?: Predicate): { found: boolean, element?: TElement } { let found = false; let result: TElement | undefined = undefined; if (predicate) { for (const element of this) { if (predicate(element)) { found = true; result = element; } } } else { for (const element of this) { found = true; result = element; } } return { found, element: result }; } last(predicate?: Predicate) { const result = this.#tryGetLast(predicate); if (result.found) { return result.element!; } throw new Error("No element was found."); } lastOrDefault(predicate?: Predicate, def?: TElement) { const result = this.#tryGetLast(predicate); return result.found ? result.element : def; } #tryGetSingle(predicate?: Predicate): { found: boolean, element?: TElement, reason?: number } { if (predicate) { let result: { found: boolean; element: TElement; } | undefined = undefined; for (const element of this) { if (predicate(element)) { if (result) { return { found: false, reason: 2 }; } result = { found: true, element }; } } } else { const iterator = this.iterator(); let next = iterator.next(); if (!next.done) { const result = { found: true, element: next.value }; next = iterator.next(); if (next.done) { return result; } return { found: false, reason: 2 }; } } return { found: false, reason: 1 }; } single(predicate?: Predicate) { const result = this.#tryGetSingle(predicate); if (result.found) { return result.element!; } let reason: string; switch (result.reason!) { case 1: reason = "No element was found."; break; case 2: reason = "More than one element was found."; break; default: reason = ""; } throw new Error(reason); } singleOrDefault(predicate?: Predicate, def?: TElement) { const result = this.#tryGetSingle(predicate); return result.found ? result.element : def; } #tryElementAt(index: number) { let i = index; for (const element of this) { if (i === 0) { return { found: true, element } as const; } i--; } return { found: false } as const; } elementAt(index: number) { const result = this.#tryElementAt(index); if (result.found) { return result.element; } throw new Error("No element found at given index."); } elementAtOrDefault(index: number, def?: TElement) { const result = this.#tryElementAt(index); return result.found ? result.element : def; } aggregate(accumulator: Accumulator, seed?: TAccumulator, resultSelector?: Converter) { const iterator = this.iterator(); if (seed === undefined) { const next = iterator.next(); if (next.done) { throw new Error("Aggregation requires at least one element."); } seed = next.value as unknown as TAccumulator; } let acc = seed; while (true) { const next = iterator.next(); if (next.done) { break; } acc = accumulator(acc, next.value); } if (resultSelector) { return resultSelector(acc); } return acc as unknown as TResult; } #find(sorter: Predicate, selector?: Converter, comparer?: Comparer) { const iterator = this.iterator(); let next = iterator.next(); if (next.done) { throw new Error("Sequence contains no element.") } if (!selector) { selector = identity as Converter; } if (!comparer) { comparer = operatorCompare; } let result = next.value; let convertedResult = selector(result); while (true) { next = iterator.next(); if (next.done) { break; } const value = next.value; const convertedValue = selector(value); if (sorter(comparer(convertedResult, convertedValue))) { result = value; convertedResult = convertedValue; } } return result; } min(comparer?: Comparer) { return this.#find(x => x > 0, undefined, comparer); } minBy(converter: Converter, comparer?: Comparer) { return this.#find(x => x > 0, converter, comparer); } max(comparer?: Comparer) { return this.#find(x => x < 0, undefined, comparer); } maxBy(converter: Converter, comparer?: Comparer) { return this.#find(x => x < 0, converter, comparer); } order(comparer?: Comparer): OrderedSequence { return new OrderSequence(this, false, comparer); } orderBy(selector: Converter, comparer?: Comparer): OrderedSequence { return new OrderBySequence(this, false, selector, comparer); } orderDescending(comparer?: Comparer): OrderedSequence { return new OrderSequence(this, true, comparer); } orderByDescending(selector: Converter, comparer?: Comparer): OrderedSequence { 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); } distinctBy(selector: Converter, equater?: Equater): Sequence { return new DistinctBySequence(this, selector, equater); } union(sequence: Sequence, equater?: Equater): Sequence { return new UnionSequence(this, sequence, equater); } unionBy(sequence: Sequence, selector: Converter, equater?: Equater): Sequence { return new UnionBySequence(this, sequence, selector, equater); } except(sequence: Sequence): Sequence { return new ExceptSequence(this, sequence); } exceptBy(sequence: Sequence, selector: Converter): Sequence { return new ExceptBySequence(this, sequence, selector); } intersect(sequence: Sequence): Sequence { return new IntersectSequence(this, sequence); } intersectBy(sequence: Sequence, selector: Converter): Sequence { return new IntersectBySequence(this, sequence, selector); } all(predicate: Predicate) { const n = this.nonEnumeratedCount(); if (n === 0) { return false; } for (const element of this) { if (!predicate(element)) { return false; } } return true; } any(predicate?: Predicate) { const n = this.nonEnumeratedCount(); if (n === 0) { return false; } if (predicate) { for (const element of this) { if (predicate(element)) { return true; } } return false; } return n < 0 ? !this.iterator().next().done : n > 0; } none(predicate?: Predicate) { const n = this.nonEnumeratedCount(); if (n === 0) { return true; } if (predicate) { for (const element of this) { if (predicate(element)) { return false; } } return true; } return n < 0 && this.iterator().next().done === true; } skip(n: number): Sequence { if (n < 0) { throw new Error("Cannot skip a negative number of elements."); } return n === 0 ? this : new SkipSequence(this, n); } skipLast(n: number): Sequence { if (n < 0) { throw new Error("Cannot skip a negative number of elements."); } return n === 0 ? this : new SkipLastSequence(this, n); } skipWhile(predicate: Predicate): Sequence { return new SkipWhileSequence(this, predicate); } take(n: number): Sequence { if (n < 0) { throw new Error("Cannot take a negative number of elements."); } return n === 0 ? empty() : new TakeSequence(this, n); } takeLast(n: number): Sequence { if (n < 0) { throw new Error("Cannot take a negative number of elements."); } return n === 0 ? empty() : new TakeLastSequence(this, n); } takeWhile(predicate: Predicate): Sequence { return new TakeWhileSequence(this, predicate); } peek(action: Action): Sequence { return new PeekSequence(this, action); } forEach(action: Action) { for (const element of this) { action(element); } } zip(sequence: Sequence): Sequence<[TElement, TOther]> { return new ZippedSequence(this, sequence); } indexed(): Sequence<[number, TElement]> { return new IndexedSequence(this); } reversed(): Sequence { return new ReversedSequence(this); } chunked(size: number): Sequence { if (size <= 0) { throw new Error("Chunk size must be positive."); } return new ChunkedSequence(this, size); } random(options?: RandomOptions) { return getRandomElement(this, options).element; } cached(): Sequence { return new CacheSequence(this); } awaited(): AsyncSequence> { return new AwaitedSequence(this); } asArray() { return this.toArray(); } toArray() { return Array.from(this); } toMap(keySelector: Converter, valueSelector: Converter) { const map = new Map(); for (const element of this) { const key = keySelector(element); const value = valueSelector(element); map.set(key, value); } return map; } toSet() { return new Set(this); } toObject(keySelector: Converter, valueSelector: Converter) { const obj: Record = {}; for (const element of this) { const key = keySelector(element); const value = valueSelector(element); obj[key] = value; } return obj; } collect(collector: Collector) { const acc = collector.initialize(); for (const e of this) { collector.accumulate(acc, e); } return collector.finalize(acc); } } export class DelegatedSequence extends SequenceMarker implements Sequence { #sequence: Sequence; constructor(sequence: Sequence) { super(); this.#sequence = sequence; } get sequence() { return this.#sequence; } protected set sequence(value: Sequence) { this.#sequence = value; } [Symbol.iterator]() { return this.iterator(); } iterator() { return this.#sequence.iterator(); } apply(pipeline: (sequence: Sequence) => TResult) { return this.#sequence.apply(pipeline); } count(predicate?: Predicate) { return this.#sequence.count(predicate); } nonEnumeratedCount() { return this.#sequence.nonEnumeratedCount(); } fastCount() { return this.#sequence.fastCount(); } maxCount() { return this.#sequence.maxCount(); } select(selector: Converter) { return this.#sequence.select(selector); } selectMany(selector: Converter>) { return this.#sequence.selectMany(selector); } where(predicate: FilterPredicate) { return this.#sequence.where(predicate); } groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: Equater | undefined): Sequence>; groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: Equater | undefined): Sequence>; groupBy(keySelector: any, elementSelector?: any, keyComparer?: any) { return this.#sequence.groupBy(keySelector, elementSelector, keyComparer); } join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater | undefined): Sequence<[TElement, TOther]>; join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: Equater | undefined): Sequence; join(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.join(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater | undefined): Sequence>; groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: Equater | undefined): Sequence; groupJoin(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.groupJoin(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } contains(obj: TElement, equater?: Equater) { return this.#sequence.contains(obj, equater); } sequenceEquals(sequence: Sequence, equater?: Equater) { return this.#sequence.sequenceEquals(sequence, equater); } append(obj: TElement) { return this.#sequence.append(obj); } prepend(obj: TElement) { return this.#sequence.prepend(obj); } remove(obj: TElement, all?: boolean, equater?: Equater) { return this.#sequence.remove(obj, all, equater); } concat(...sequences: Sequence[]) { return this.#sequence.concat(...sequences); } first(predicate?: Predicate) { return this.#sequence.first(predicate); } firstOrDefault(predicate?: Predicate, def?: TElement) { return this.#sequence.firstOrDefault(predicate, def); } last(predicate?: Predicate) { return this.#sequence.last(predicate); } lastOrDefault(predicate?: Predicate, def?: TElement) { return this.#sequence.lastOrDefault(predicate, def); } single(predicate?: Predicate) { return this.#sequence.single(predicate); } singleOrDefault(predicate?: Predicate, def?: TElement) { return this.#sequence.singleOrDefault(predicate, def); } elementAt(index: number) { return this.#sequence.elementAt(index); } elementAtOrDefault(index: number, def?: TElement) { return this.#sequence.elementAtOrDefault(index, def); } aggregate(accumulator: Accumulator, seed?: TAccumulator, resultSelector?: Converter) { return this.#sequence.aggregate(accumulator, seed, resultSelector); } min(comparer?: Comparer) { return this.#sequence.min(comparer); } minBy(selector: Converter, comparer?: Comparer) { return this.#sequence.minBy(selector, comparer); } max(comparer?: Comparer) { return this.#sequence.max(comparer); } maxBy(selector: Converter, comparer?: Comparer) { return this.#sequence.maxBy(selector, comparer); } order(comparer?: Comparer) { return this.#sequence.order(comparer); } orderBy(selector: Converter, comparer?: Comparer) { return this.#sequence.orderBy(selector, comparer); } orderDescending(comparer?: Comparer) { return this.#sequence.orderDescending(comparer); } orderByDescending(selector: Converter, comparer?: Comparer) { 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); } distinctBy(selector: Converter, equater?: Equater) { return this.#sequence.distinctBy(selector, equater); } union(sequence: Sequence, equater?: Equater) { return this.#sequence.union(sequence, equater); } unionBy(sequence: Sequence, selector: Converter, equater?: Equater) { return this.#sequence.unionBy(sequence, selector, equater); } except(sequence: Sequence, equater?: Equater) { return this.#sequence.except(sequence, equater); } exceptBy(sequence: Sequence, selector: Converter, equater?: Equater) { return this.#sequence.exceptBy(sequence, selector, equater); } intersect(sequence: Sequence, equater?: Equater) { return this.#sequence.intersect(sequence, equater); } intersectBy(sequence: Sequence, selector: Converter, equater?: Equater) { return this.#sequence.intersectBy(sequence, selector, equater); } all(predicate: Predicate) { return this.#sequence.all(predicate); } any(predicate: Predicate): boolean; any(): boolean; any(predicate?: any) { return this.#sequence.any(predicate); } none(predicate: Predicate): boolean; none(): boolean; none(predicate?: any) { return this.#sequence.none(predicate); } skip(n: number) { return this.#sequence.skip(n); } skipLast(n: number) { return this.#sequence.skipLast(n); } skipWhile(condition: Predicate) { return this.#sequence.skipWhile(condition); } take(n: number) { return this.#sequence.take(n); } takeLast(n: number) { return this.#sequence.takeLast(n); } takeWhile(condition: Predicate) { return this.#sequence.takeWhile(condition); } peek(action: Action) { return this.#sequence.peek(action); } forEach(action: Action) { this.#sequence.forEach(action); } zip(sequence: Sequence) { return this.#sequence.zip(sequence); } indexed() { return this.#sequence.indexed(); } reversed() { return this.#sequence.reversed(); } chunked(size: number) { return this.#sequence.chunked(size); } random(options?: RandomOptions) { return this.#sequence.random(options); } cached() { return this.#sequence.cached(); } awaited(): AsyncSequence> { return this.#sequence.awaited(); } asArray() { return this.#sequence.asArray(); } toArray() { return this.#sequence.toArray(); } toMap(keySelector: Converter, valueSelector: Converter) { return this.#sequence.toMap(keySelector, valueSelector); } toSet() { return this.#sequence.toSet(); } toObject(keySelector: Converter, valueSelector: Converter) { return this.#sequence.toObject(keySelector, valueSelector); } collect(collector: Collector) { return this.#sequence.collect(collector); } } export class GroupedSequenceImpl extends DelegatedSequence implements GroupedSequence { readonly #key: TKey; constructor(key: TKey, grouping: Sequence) { super(grouping); this.#key = key; } public get key() { return this.#key; } } export abstract class BaseOrderedSequence extends BaseSequence implements OrderedSequence { readonly #sequence: Sequence; readonly #sorter: Comparer | undefined; readonly #descending: boolean; constructor(sequence: Sequence, sorter: Comparer | undefined, descending: boolean) { super(); this.#sequence = sequence; this.#sorter = sorter; this.#descending = descending; } override nonEnumeratedCount() { return this.#sequence.nonEnumeratedCount(); } override maxCount() { return this.#sequence.maxCount(); } get comparer() { return this.#sorter; } thenSelf(comparer?: Comparer): OrderedSequence { return new ThenOrderSequence(this, false, comparer); } thenBy(selector: Converter, comparer?: Comparer): OrderedSequence { return new ThenOrderBySequence(this, false, selector, comparer); } thenSelfDescending(comparer?: Comparer): OrderedSequence { return new ThenOrderSequence(this, true, comparer); } thenByDescending(selector: Converter, comparer?: Comparer): OrderedSequence { return new ThenOrderBySequence(this, true, selector, comparer); } override *iterator() { const arr: TElement[] = []; for (const obj of this.#sequence) { arr.push(obj); } if (this.#sorter) { arr.sort(this.#descending ? reverseComparer(this.#sorter) : this.#sorter); } else { arr.sort(); if (this.#descending) { arr.reverse(); } } yield* arr; } } class EmptySequence extends BaseSequence { override nonEnumeratedCount() { return 0; } override *iterator() { } } export const EMPTY = new EmptySequence(); export class RangeSequence extends BaseSequence { readonly #min: number; readonly #max: number; readonly #step: number; constructor(min: number, max: number, step: number) { super(); this.#min = min; this.#max = max; this.#step = step; } get length() { return Math.ceil((this.#max - this.#min) / this.#step); } override nonEnumeratedCount() { return this.length; } override maxCount() { return this.length; } override *iterator() { for (let i = this.#min; i < this.#max; i += this.#step) { yield i; } } } export class BigIntRangeSequence extends BaseSequence { readonly #min: bigint; readonly #max: bigint; readonly #step: bigint; constructor(min: bigint, max: bigint, step: bigint) { super(); this.#min = min; this.#max = max; this.#step = step; } get length() { return Math.ceil(Number((this.#max - this.#min) / this.#step)); } override nonEnumeratedCount() { return this.length; } override maxCount() { return this.length; } override *iterator() { for (let i = this.#min; i < this.#max; i += this.#step) { yield i; } } } export class RepeatSequence extends BaseSequence { readonly #value: T; readonly #count: number; constructor(value: T, count: number) { super(); this.#value = value; this.#count = count; } override nonEnumeratedCount() { return this.#count; } override maxCount() { return this.#count; } override *iterator() { let i = this.#count; while (i-- > 0) { yield this.#value; } } } export class RepeatForeverSequence extends BaseSequence { readonly #value: T; constructor(value: T) { super(); this.#value = value; } override nonEnumeratedCount() { return Infinity; } override maxCount() { return Infinity; } override *iterator() { while (true) { yield this.#value; } } } export class WrappedObject extends BaseSequence { readonly #obj: T; constructor(obj: T) { super(); this.#obj = obj; } override nonEnumeratedCount() { return 1; } override maxCount() { return 1; } override *iterator() { yield this.#obj; } } export class WrappedIterable extends BaseSequence { readonly #iterable: Iterable; constructor(iterable: Iterable) { super(); this.#iterable = iterable; } override iterator() { return this.#iterable[Symbol.iterator](); } } export class WrappedArray extends BaseSequence { readonly #array: T[]; constructor(array: T[]) { super(); this.#array = array; } override nonEnumeratedCount() { return this.#array.length; } override maxCount() { return this.#array.length; } override contains(obj: T, equater?: Equater): boolean { if (equater) { return this.#array.some(x => equater(x, obj)); } return this.#array.includes(obj); } override all(predicate: Predicate) { return this.#array.every(predicate); } override any(predicate?: Predicate) { if (predicate) { return this.#array.some(predicate); } return this.#array.length > 0; } override none(predicate?: Predicate) { if (predicate) { return !this.#array.some(predicate); } return this.#array.length === 0; } override asArray() { return this.#array; } override toArray() { return this.#array.slice(); } override *iterator() { const length = this.#array.length; for (let i = 0; i < length; i++) { if (i in this.#array) { yield this.#array[i]; } } } } export class WrappedArrayLike extends BaseSequence { readonly #arrayLike: ArrayLike; constructor(arrayLike: ArrayLike) { super(); this.#arrayLike = arrayLike; } get arrayLike() { return this.#arrayLike; } override nonEnumeratedCount() { return this.#arrayLike.length; } override maxCount() { return this.#arrayLike.length; } override toArray() { return Array.from(this.#arrayLike); } override *iterator() { const length = this.#arrayLike.length; for (let i = 0; i < length; i++) { if (i in this.#arrayLike) { yield this.#arrayLike[i]; } } } } export class WrappedSet extends BaseSequence { readonly #set: Set; constructor(set: Set) { super(); this.#set = set; } override nonEnumeratedCount() { return this.#set.size; } override maxCount() { return this.#set.size; } override contains(obj: T, equater?: Equater) { if (equater) { return super.contains(obj, equater); } return this.#set.has(obj); } override iterator() { return this.#set.values(); } } export class WrappedMap extends BaseSequence<[K, V]> { readonly #map: Map; constructor(map: Map) { super(); this.#map = map; } override nonEnumeratedCount() { return this.#map.size; } override maxCount() { return this.#map.size; } override contains(obj: [K, V], equater?: Equater<[K, V]>) { if (equater) { return super.contains(obj, equater); } if (!this.#map.has(obj[0])) { return false; } return this.#map.get(obj[0]) === obj[1]; } override iterator() { return this.#map.entries(); } } export class GeneratorSequence extends BaseSequence { readonly #generator: () => Iterable; constructor(generator: () => Iterable) { super(); this.#generator = generator; } override iterator() { return this.#generator()[Symbol.iterator](); } } export class FunctionSequence extends BaseSequence { readonly #f: () => T; constructor(f: () => T) { super(); this.#f = f; } override nonEnumeratedCount() { return Infinity; } override maxCount() { return Infinity; } override *iterator() { while (true) { yield this.#f(); } } } export class ConcatSequence extends BaseSequence { readonly #sequences: Iterable>; constructor(sequences: Iterable>) { super(); this.#sequences = sequences; } override nonEnumeratedCount() { let n = 0; for (const sequence of this.#sequences) { const m = sequence.nonEnumeratedCount(); if (m < 0) { return super.nonEnumeratedCount(); } n += m; } return n; } override count() { let n = 0; for (const sequence of this.#sequences) { n += sequence.count(); } return n; } override maxCount() { let n = 0; for (const sequence of this.#sequences) { n += sequence.maxCount(); } return n; } override *iterator() { for (const sequence of this.#sequences) { yield* sequence; } } } class DistinctSequence extends BaseSequence { readonly #sequence: Sequence; readonly #equater: Equater | undefined; constructor(sequence: Sequence, equater?: Equater) { super(); this.#sequence = sequence; this.#equater = equater; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#sequence) { if (set.add(obj)) { yield obj; } } } } class DistinctBySequence extends BaseSequence { readonly #sequence: Sequence; readonly #selector: Converter; readonly #equater: Equater | undefined; constructor(sequence: Sequence, selector: Converter, equater?: Equater) { super(); this.#sequence = sequence; this.#selector = selector; this.#equater = equater; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#sequence) { if (set.add(this.#selector(obj))) { yield obj; } } } } class WhereSequence extends BaseSequence { readonly #sequence: Sequence; readonly #predicate: FilterPredicate; constructor(sequence: Sequence, predicate: FilterPredicate) { super(); this.#sequence = sequence; this.#predicate = predicate; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { for (const obj of this.#sequence) { if (this.#predicate(obj)) { yield obj; } } } } class SelectManySequence extends BaseSequence { readonly #sequence: Sequence; readonly #converter: Converter>; constructor(sequence: Sequence, converter: Converter>) { super(); this.#sequence = sequence; this.#converter = converter; } override *iterator() { for (const obj of this.#sequence) { yield* this.#converter(obj); } } } class IndexedSequence extends BaseSequence<[number, T]> { readonly #sequence: Sequence; constructor(sequence: Sequence) { super(); this.#sequence = sequence; } override nonEnumeratedCount() { return this.#sequence.nonEnumeratedCount(); } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { let i = 0; for (const obj of this.#sequence) { yield [i++, obj] as [number, T]; } } } class SelectSequence extends BaseSequence { readonly #sequence: Sequence; readonly #converter: Converter; constructor(sequence: Sequence, converter: Converter) { super(); this.#sequence = sequence; this.#converter = converter; } override nonEnumeratedCount() { return this.#sequence.nonEnumeratedCount(); } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { for (const obj of this.#sequence) { yield this.#converter(obj); } } } class SkipWhileSequence extends BaseSequence { readonly #sequence: Sequence; readonly #predicate: Predicate; constructor(sequence: Sequence, predicate: Predicate) { super(); this.#sequence = sequence; this.#predicate = predicate; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { const e = wrapAsIterable(this.#sequence.iterator()); for (const obj of e) { if (!this.#predicate(obj)) { yield obj; break; } } yield* e; } } class SkipLastSequence extends BaseSequence { readonly #sequence: Sequence; readonly #n: number; constructor(sequence: Sequence, n: number) { super(); this.#sequence = sequence; this.#n = n; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : Math.max(0, n - this.#n); } override maxCount() { return Math.max(0, this.#sequence.maxCount() - this.#n); } override *iterator() { const iterator = this.#sequence.iterator(); const buffer = new Array(this.#n); // n > 0 let i = 0; do { const next = iterator.next(); if (next.done) { return; } buffer[i++] = next.value; } while (i < this.#n); i = 0; for (const obj of wrapAsIterable(iterator)) { yield buffer[i]; buffer[i] = obj; i = (i + 1) % this.#n; } } } class SkipSequence extends BaseSequence { readonly #sequence: Sequence; readonly #n: number; constructor(sequence: Sequence, n: number) { super(); this.#sequence = sequence; this.#n = n; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : Math.max(0, n - this.#n); } override maxCount() { return Math.max(0, this.#sequence.maxCount() - this.#n); } override *iterator() { const iterator = this.#sequence.iterator(); let i = 0; do { if (iterator.next().done) { return; } i++; } while (i < this.#n); yield* wrapAsIterable(iterator); } } class TakeWhileSequence extends BaseSequence { readonly #sequence: Sequence; readonly #predicate: Predicate; constructor(sequence: Sequence, predicate: Predicate) { super(); this.#sequence = sequence; this.#predicate = predicate; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { for (const obj of this.#sequence) { if (!this.#predicate(obj)) { return; } yield obj; } } } class TakeLastSequence extends BaseSequence { readonly #sequence: Sequence; readonly #n: number; constructor(sequence: Sequence, n: number) { super(); this.#sequence = sequence; this.#n = n; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : Math.min(this.#n, n); } override maxCount() { return Math.min(this.#n, this.#sequence.maxCount()); } override *iterator() { const queue = createQueue(this.#n); for (const obj of this.#sequence) { queue.enqueue(obj); } yield* queue; } } class TakeSequence extends BaseSequence { readonly #sequence: Sequence; readonly #n: number; constructor(sequence: Sequence, n: number) { super(); this.#sequence = sequence; this.#n = n; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : Math.min(this.#n, n); } override maxCount() { return Math.min(this.#n, this.#sequence.maxCount()); } override *iterator() { const iterator = this.#sequence.iterator(); let i = this.#n; while (i > 0) { const next = iterator.next(); if (next.done) { return; } yield next.value; i--; } } } class OrderSequence extends BaseOrderedSequence { constructor(sequence: Sequence, descending: boolean, sorter?: Comparer) { super(sequence, sorter, descending); } } class OrderBySequence extends BaseOrderedSequence { constructor(sequence: Sequence, descending: boolean, selector: Converter, sorter?: Comparer) { super(sequence, OrderBySequence.#createSorter(selector, sorter), descending); } static #createSorter(selector: Converter, sorter?: Comparer) { const _sorter = sorter ?? defaultArrayComparer; return (a: T, b: T) => _sorter(selector(a), selector(b)); } } class ThenOrderSequence extends BaseOrderedSequence { constructor(sequence: OrderedSequence, descending: boolean, sorter?: Comparer) { super(sequence, combineComparers(sequence.comparer ?? defaultArrayComparer, sorter ?? defaultArrayComparer), descending); } } class ThenOrderBySequence extends BaseOrderedSequence { constructor(sequence: OrderedSequence, descending: boolean, selector: Converter, sorter?: Comparer) { super(sequence, ThenOrderBySequence.#createCombinedSorter(sequence.comparer, selector, sorter), descending); } static #createCombinedSorter(baseSorter: Comparer | undefined, selector: Converter, sorter?: Comparer) { const _baseSorter = baseSorter ?? defaultArrayComparer; const _sorter = sorter ?? defaultArrayComparer; return combineComparers(_baseSorter, (a: T, b: T) => _sorter(selector(a), selector(b))); } } class AppendSequence extends BaseSequence { readonly #sequence: Sequence; readonly #obj: T; constructor(sequence: Sequence, obj: T) { super(); this.#sequence = sequence; this.#obj = obj; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : n + 1; } override maxCount() { return this.#sequence.maxCount() + 1; } override *iterator() { yield* this.#sequence; yield this.#obj; } } class PrependSequence extends BaseSequence { readonly #sequence: Sequence; readonly #obj: T; constructor(sequence: Sequence, obj: T) { super(); this.#sequence = sequence; this.#obj = obj; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : n + 1; } override maxCount() { return this.#sequence.maxCount() + 1; } override *iterator() { yield this.#obj; yield* this.#sequence; } } class PeekSequence extends DelegatedSequence { readonly #action: Action; constructor(sequence: Sequence, action: Action) { super(sequence); this.#action = action; } override *iterator() { for (const obj of wrapAsIterable(super.iterator())) { this.#action(obj); yield obj; } } } class ZippedSequence extends BaseSequence<[T, U]> { readonly #first: Sequence; readonly #second: Sequence; constructor(first: Sequence, second: Sequence) { super(); this.#first = first; this.#second = second; } override nonEnumeratedCount() { const first = this.#first.nonEnumeratedCount(); const second = this.#second.nonEnumeratedCount(); return first < 0 || second < 0 ? super.nonEnumeratedCount() : Math.min(first, second); } override maxCount() { return Math.min(this.#first.maxCount(), this.#second.maxCount()); } override *iterator() { const firstIterator = this.#first.iterator(); const secondIterator = this.#second.iterator(); while (true) { const firstNext = firstIterator.next(); const secondNext = secondIterator.next(); if (firstNext.done || secondNext.done) { return; } yield [firstNext.value, secondNext.value] as [T, U]; } } } class UnionSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#equater = equater; } override maxCount() { return this.#first.maxCount() + this.#second.maxCount(); } *#iterator() { yield* this.#first; yield* this.#second; } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#iterator()) { if (set.add(obj)) { yield obj; } } } } class UnionBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#selector = selector; this.#equater = equater; } override maxCount() { return this.#first.maxCount() + this.#second.maxCount(); } *#iterator() { yield* this.#first; yield* this.#second; } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#iterator()) { if (set.add(this.#selector(obj))) { yield obj; } } } } class ExceptSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#equater = equater; } override maxCount() { return this.#first.maxCount(); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#second) { set.add(obj); } for (const obj of this.#first) { if (set.add(obj)) { yield obj; } } } } class ExceptBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#selector = selector; this.#equater = equater; } override maxCount() { return this.#first.maxCount(); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#second) { set.add(this.#selector(obj)); } for (const obj of this.#first) { if (set.add(this.#selector(obj))) { yield obj; } } } } class IntersectSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#equater = equater; } override maxCount() { return Math.min(this.#first.maxCount(), this.#second.maxCount()); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#second) { set.add(obj); } for (const obj of this.#first) { if (set.remove(obj)) { yield obj; } } } } class IntersectBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; readonly #equater: Equater | undefined; constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { super(); this.#first = first; this.#second = second; this.#selector = selector; this.#equater = equater; } override maxCount() { return Math.min(this.#first.maxCount(), this.#second.maxCount()); } override *iterator() { const set = createEqualitySet(this.#equater); for (const obj of this.#second) { set.add(this.#selector(obj)); } for (const obj of this.#first) { if (set.remove(this.#selector(obj))) { yield obj; } } } } class ReversedSequence extends BaseSequence { readonly #sequence: Sequence; constructor(sequence: Sequence) { super(); this.#sequence = sequence; } override nonEnumeratedCount() { return this.#sequence.nonEnumeratedCount(); } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { const buffer: T[] = []; for (const obj of this.#sequence) { buffer.push(obj); } while (buffer.length > 0) { yield buffer.pop()!; } } } class GroupBySequence extends BaseSequence> { readonly #sequence: Sequence; readonly #keySelector: Converter; readonly #elementSelector: Converter; readonly #keyComparer: Equater | undefined; constructor(sequence: Sequence, keySelector: Converter, elementSelector?: Converter, keyComparer?: Equater) { super(); this.#sequence = sequence; this.#keySelector = keySelector; this.#elementSelector = elementSelector ?? identity as Converter; this.#keyComparer = keyComparer; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { const groupings = createEqualityMap(this.#keyComparer); for (const obj of this.#sequence) { const key = this.#keySelector(obj); let grouping = groupings.get(key); if (!grouping) { groupings.set(key, grouping = []); } grouping.push(this.#elementSelector(obj)); } for (const entry of groupings) { yield new GroupedSequenceImpl(entry[0], array(entry[1])); } } } class ChunkedSequence extends BaseSequence { readonly #sequence: Sequence; readonly #size: number; constructor(sequence: Sequence, size: number) { super(); this.#sequence = sequence; this.#size = size; } override nonEnumeratedCount() { const n = this.#sequence.nonEnumeratedCount(); return n < 0 ? super.nonEnumeratedCount() : Math.ceil(n / this.#size); } override maxCount() { return Math.ceil(this.#sequence.maxCount() / this.#size); } override *iterator() { let chunk: T[] = []; for (const obj of this.#sequence) { chunk.push(obj); if (chunk.length === this.#size) { yield chunk; chunk = []; } } if (chunk.length > 0) { yield chunk; } } } class JoinSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #firstKeySelector: Converter; readonly #secondKeySelector: Converter; readonly #resultSelector: BiConverter; readonly #keyComparer: Equater; constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: Equater) { super(); this.#first = first; this.#second = second; this.#firstKeySelector = firstKeySelector; this.#secondKeySelector = secondKeySelector; this.#resultSelector = resultSelector ?? JoinSequence.#defaultResultSelector as BiConverter; this.#keyComparer = keyComparer ?? strictEquals; } static #defaultResultSelector(first: TOuter, second: TInner): [TOuter, TInner] { return [first, second]; } override maxCount() { return this.#first.maxCount() * this.#second.maxCount(); } override *iterator() { for (const firstObj of this.#first) { const firstKey = this.#firstKeySelector(firstObj); for (const secondObj of this.#second) { const secondKey = this.#secondKeySelector(secondObj); if (this.#keyComparer(firstKey, secondKey)) { yield this.#resultSelector(firstObj, secondObj); } } } } } class GroupJoinSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #firstKeySelector: Converter; readonly #secondKeySelector: Converter; readonly #resultSelector: BiConverter, TResult>; readonly #keyComparer: Equater; constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: Equater) { super(); this.#first = first; this.#second = second; this.#firstKeySelector = firstKeySelector; this.#secondKeySelector = secondKeySelector; this.#resultSelector = resultSelector ?? GroupJoinSequence.#defaultResultSelector as BiConverter, TResult>; this.#keyComparer = keyComparer ?? strictEquals; } static #defaultResultSelector(first: TOuter, second: Sequence) { return new GroupedSequenceImpl(first, second); } override nonEnumeratedCount() { return this.#first.nonEnumeratedCount(); } override maxCount() { return this.#first.maxCount(); } override *iterator() { for (const firstObj of this.#first) { const firstKey = this.#firstKeySelector(firstObj); const secondObjs: TInner[] = []; for (const secondObj of this.#second) { const secondKey = this.#secondKeySelector(secondObj); if (this.#keyComparer(firstKey, secondKey)) { secondObjs.push(secondObj); } } yield this.#resultSelector(firstObj, array(secondObjs)); } } } class RemoveSequence extends BaseSequence { readonly #sequence: Sequence; readonly #obj: T; readonly #all: boolean; readonly #equater: Equater; constructor(sequence: Sequence, obj: T, all?: boolean, equater?: Equater) { super(); this.#sequence = sequence; this.#obj = obj; this.#all = all ?? false; this.#equater = equater ?? strictEquals; } override maxCount() { return this.#sequence.maxCount(); } override *iterator() { let gotOne = false; for (const obj of this.#sequence) { if (this.#equater(this.#obj, obj)) { if (this.#all) { continue; } if (!gotOne) { gotOne = true; continue; } } yield obj; } } } class CacheSequence extends DelegatedSequence { #cached = false; constructor(sequence: Sequence) { super(sequence); } override iterator() { if (!this.#cached) { this.sequence = array(this.sequence.toArray()); this.#cached = true; } 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(); } } class AwaitedSequence extends BaseAsyncSequence> { readonly #sequence: Sequence; constructor(sequence: Sequence) { super(); this.#sequence = sequence; } override async *iterator() { yield* this.#sequence; } }