import { PipeBody, PipeFunction } from "../../pipables"; import { tryGetCountDirect } from "./count"; import { asLinq, LinqWrapper } from "../linqWrapper"; import { IntermediateLinqWrapper, IterableFactoryLinqWrapper } from "../internal"; import { BuiltInLinqTraits, TryGetCountDirectSymbol } from "../traits"; import { IndexedSequenceElementSelector } from "./typing"; export function select(selector: IndexedSequenceElementSelector): PipeBody, LinqWrapper> { return target => { if (target instanceof SelectLinqWrapper) { const state = target.__state; return new SelectLinqWrapper({ ...state, selectors: [ ...state.selectors, { selector: selector as IndexedSequenceElementSelector }, ], }); } return new SelectLinqWrapper({ iterable: target.unwrap(), selectors: [{ selector: selector as IndexedSequenceElementSelector }], }); }; } select satisfies PipeFunction; export function selectMany(selector: IndexedSequenceElementSelector>): PipeBody, LinqWrapper> { return target => { const unwrapped = target.unwrap(); return new IterableFactoryLinqWrapper(() => selectManyIterable(unwrapped, selector)); }; } selectMany satisfies PipeFunction; function* selectManyIterable(iterable: Iterable, selector: IndexedSequenceElementSelector>): Iterable { let index = 0; for (const e of iterable) { yield* selector(e, index); index++; } } interface SelectorEntry { selector: IndexedSequenceElementSelector; // Reserved for future use. flatten?: false; } /* TODO interface FlattenSelectorEntry { selector: SequenceElementSelector>; flatten: true; } */ interface SelectIteratorState { readonly iterable: Iterable; selectors: SelectorEntry[]; } /* interface SelectManyStackItem { selectorIndex: number; iterator: Iterator; } */ class SelectLinqWrapper extends IntermediateLinqWrapper> implements BuiltInLinqTraits { public override *[Symbol.iterator](): Iterator { const { iterable, selectors } = this.__state; const indices = new Array(selectors.length).fill(0); for (const e of iterable) { let projection: unknown = e; for (let i = 0; i < selectors.length; i++) { projection = selectors[i].selector(projection, indices[i]); indices[i]++; } yield projection as TResult; } } public [TryGetCountDirectSymbol](): number | undefined { // projection does not change item count. // N.b. This does not hold as soon as we introduce selectMany here. return asLinq(this.__state.iterable).$(tryGetCountDirect()); } }