From a5866c08ffbc2513112fe8937efbebaf744ecd07 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sat, 25 Apr 2026 20:55:50 +0200 Subject: [PATCH] Implement sequence --- src/index.ts | 2 +- src/sequence.ts | 560 +++++++++++++++---------------- src/sequence/average.ts | 4 +- src/sequence/averageBy.ts | 4 +- src/sequence/averageByOrNull.ts | 9 + src/sequence/averageOrNull.ts | 9 + src/sequence/chunked.ts | 3 + src/sequence/collect.ts | 3 - src/sequence/containsAll.ts | 8 +- src/sequence/drop.ts | 10 +- src/sequence/dropUntil.ts | 13 +- src/sequence/dropWhile.ts | 13 +- src/sequence/filterIndexed.ts | 6 - src/sequence/first.ts | 2 +- src/sequence/firstIndex.ts | 10 - src/sequence/firstIndexOrNull.ts | 8 - src/sequence/flatMapIndexed.ts | 6 - src/sequence/get.ts | 3 +- src/sequence/getOrNull.ts | 1 + src/sequence/indices.ts | 2 +- src/sequence/intersect.ts | 6 + src/sequence/last.ts | 11 +- src/sequence/lastIndex.ts | 12 - src/sequence/lastIndexOrNull.ts | 10 - src/sequence/lastOrNull.ts | 9 +- src/sequence/mapIndexed.ts | 6 - src/sequence/mapNotNull.ts | 6 + src/sequence/max.ts | 11 +- src/sequence/maxBy.ts | 19 +- src/sequence/maxByOrNull.ts | 17 +- src/sequence/maxOf.ts | 11 +- src/sequence/maxOfOrNull.ts | 9 +- src/sequence/maxOrNull.ts | 9 +- src/sequence/maxWith.ts | 11 +- src/sequence/maxWithOrNull.ts | 9 +- src/sequence/min.ts | 11 +- src/sequence/minBy.ts | 15 +- src/sequence/minByOrNull.ts | 13 +- src/sequence/minOf.ts | 11 +- src/sequence/minOfOrNull.ts | 9 +- src/sequence/minOrNull.ts | 9 +- src/sequence/minWith.ts | 14 +- src/sequence/minWithOrNull.ts | 12 +- src/sequence/partition.ts | 31 +- src/sequence/plus.ts | 6 +- src/sequence/reduce.ts | 16 +- src/sequence/reduceOrNull.ts | 14 +- src/sequence/reversed.ts | 4 +- src/sequence/single.ts | 15 +- src/sequence/singleOrNull.ts | 13 +- src/sequence/slice.ts | 15 +- src/sequence/sorted.ts | 4 +- src/sequence/sortedBy.ts | 4 +- src/sequence/sortedWith.ts | 4 +- src/sequence/take.ts | 6 +- src/sequence/toArray.ts | 3 + src/sequence/toMap.ts | 3 + src/sequence/toSet.ts | 3 + src/sequence/unzip.ts | 35 +- src/sequence/windowed.ts | 13 +- src/sequence/zip.ts | 10 +- tsconfig.json | 14 +- 62 files changed, 570 insertions(+), 569 deletions(-) create mode 100644 src/sequence/averageByOrNull.ts create mode 100644 src/sequence/averageOrNull.ts delete mode 100644 src/sequence/collect.ts delete mode 100644 src/sequence/filterIndexed.ts delete mode 100644 src/sequence/firstIndex.ts delete mode 100644 src/sequence/firstIndexOrNull.ts delete mode 100644 src/sequence/flatMapIndexed.ts create mode 100644 src/sequence/intersect.ts delete mode 100644 src/sequence/lastIndex.ts delete mode 100644 src/sequence/lastIndexOrNull.ts delete mode 100644 src/sequence/mapIndexed.ts create mode 100644 src/sequence/mapNotNull.ts create mode 100644 src/sequence/toArray.ts create mode 100644 src/sequence/toMap.ts create mode 100644 src/sequence/toSet.ts diff --git a/src/index.ts b/src/index.ts index 4e20076..8daffe2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export {sequence} from "./sequence.js"; +export {sequence} from "./sequence"; diff --git a/src/sequence.ts b/src/sequence.ts index 3a147bf..011a9b6 100644 --- a/src/sequence.ts +++ b/src/sequence.ts @@ -1,100 +1,98 @@ -import {map} from "./sequence/map.js"; -import {collect} from "./sequence/collect.js"; -import {forEach} from "./sequence/forEach.js"; -import {filter} from "./sequence/filter.js"; -import {count} from "./sequence/count.js"; -import {isEmpty} from "./sequence/isEmpty.js"; -import {isNotEmpty} from "./sequence/isNotEmpty.js"; -import {indices} from "./sequence/indices.js"; -import {withIndex} from "./sequence/withIndex.js"; -import {filterIndexed} from "./sequence/filterIndexed.js"; -import {mapIndexed} from "./sequence/mapIndexed.js"; -import {any} from "./sequence/any.js"; -import {none} from "./sequence/none.js"; -import {all} from "./sequence/all.js"; -import {first} from "./sequence/first.js"; -import {firstOrNull} from "./sequence/firstOrNull.js"; -import {last} from "./sequence/last.js"; -import {lastOrNull} from "./sequence/lastOrNull.js"; -import {single} from "./sequence/single.js"; -import {singleOrNull} from "./sequence/singleOrNull.js"; -import {plus} from "./sequence/plus.js"; -import {distinct} from "./sequence/distinct.js"; -import {distinctBy} from "./sequence/distinctBy.js"; -import {drop} from "./sequence/drop.js"; -import {take} from "./sequence/take.js"; -import {slice} from "./sequence/slice.js"; -import {dropWhile} from "./sequence/dropWhile.js"; -import {dropUntil} from "./sequence/dropUntil.js"; -import {takeWhile} from "./sequence/takeWhile.js"; -import {takeUntil} from "./sequence/takeUntil.js"; -import {flatMap} from "./sequence/flatMap.js"; -import {flatMapIndexed} from "./sequence/flatMapIndexed.js"; -import {associate} from "./sequence/associate.js"; -import {associateBy} from "./sequence/associateBy.js"; -import {associateWith} from "./sequence/associateWith.js"; -import {get} from "./sequence/get.js"; -import {getOrNull} from "./sequence/getOrNull.js"; -import {sumBy} from "./sequence/sumBy.js"; -import {averageBy} from "./sequence/averageBy.js"; -import {contains} from "./sequence/contains.js"; -import {containsAll} from "./sequence/containsAll.js"; -import {minBy} from "./sequence/minBy.js"; -import {minByOrNull} from "./sequence/minByOrNull.js"; -import {maxBy} from "./sequence/maxBy.js"; -import {maxByOrNull} from "./sequence/maxByOrNull.js"; -import {minOf} from "./sequence/minOf.js"; -import {minOfOrNull} from "./sequence/minOfOrNull.js"; -import {maxOf} from "./sequence/maxOf.js"; -import {maxOfOrNull} from "./sequence/maxOfOrNull.js"; -import {minWith} from "./sequence/minWith.js"; -import {minWithOrNull} from "./sequence/minWithOrNull.js"; -import {maxWith} from "./sequence/maxWith.js"; -import {maxWithOrNull} from "./sequence/maxWithOrNull.js"; -import {minus} from "./sequence/minus.js"; -import {reduce} from "./sequence/reduce.js"; -import {reduceOrNull} from "./sequence/reduceOrNull.js"; -import {fold} from "./sequence/fold.js"; -import {scan} from "./sequence/scan.js"; -import {partition} from "./sequence/partition.js"; -import {firstIndex} from "./sequence/firstIndex.js"; -import {firstIndexOrNull} from "./sequence/firstIndexOrNull.js"; -import {lastIndex} from "./sequence/lastIndex.js"; -import {lastIndexOrNull} from "./sequence/lastIndexOrNull.js"; -import {sortedWith} from "./sequence/sortedWith.js"; -import {sortedBy} from "./sequence/sortedBy.js"; -import {reversed} from "./sequence/reversed.js"; -import {windowed} from "./sequence/windowed.js"; -import {chunked} from "./sequence/chunked.js"; -import {zip} from "./sequence/zip.js"; -import {unzip} from "./sequence/unzip.js"; -import {sum} from "./sequence/sum.js"; -import {average} from "./sequence/average.js"; -import {min} from "./sequence/min.js"; -import {minOrNull} from "./sequence/minOrNull.js"; -import {max} from "./sequence/max.js"; -import {maxOrNull} from "./sequence/maxOrNull.js"; -import {flatten} from "./sequence/flatten.js"; -import {sorted} from "./sequence/sorted.js"; -import {onEach} from "./sequence/onEach.js"; -import {joinToString} from "./sequence/joinToString.js"; -import {groupBy} from "./sequence/groupBy.js"; +import {map} from "./sequence/map"; +import {toArray} from "./sequence/toArray"; +import {forEach} from "./sequence/forEach"; +import {filter} from "./sequence/filter"; +import {count} from "./sequence/count"; +import {isEmpty} from "./sequence/isEmpty"; +import {isNotEmpty} from "./sequence/isNotEmpty"; +import {indices} from "./sequence/indices"; +import {withIndex} from "./sequence/withIndex"; +import {any} from "./sequence/any"; +import {none} from "./sequence/none"; +import {all} from "./sequence/all"; +import {first} from "./sequence/first"; +import {firstOrNull} from "./sequence/firstOrNull"; +import {last} from "./sequence/last"; +import {lastOrNull} from "./sequence/lastOrNull"; +import {single} from "./sequence/single"; +import {singleOrNull} from "./sequence/singleOrNull"; +import {plus} from "./sequence/plus"; +import {distinct} from "./sequence/distinct"; +import {distinctBy} from "./sequence/distinctBy"; +import {drop} from "./sequence/drop"; +import {take} from "./sequence/take"; +import {slice} from "./sequence/slice"; +import {dropWhile} from "./sequence/dropWhile"; +import {dropUntil} from "./sequence/dropUntil"; +import {takeWhile} from "./sequence/takeWhile"; +import {takeUntil} from "./sequence/takeUntil"; +import {flatMap} from "./sequence/flatMap"; +import {associate} from "./sequence/associate"; +import {associateBy} from "./sequence/associateBy"; +import {associateWith} from "./sequence/associateWith"; +import {get} from "./sequence/get"; +import {getOrNull} from "./sequence/getOrNull"; +import {sumBy} from "./sequence/sumBy"; +import {averageBy} from "./sequence/averageBy"; +import {contains} from "./sequence/contains"; +import {containsAll} from "./sequence/containsAll"; +import {minBy} from "./sequence/minBy"; +import {minByOrNull} from "./sequence/minByOrNull"; +import {maxBy} from "./sequence/maxBy"; +import {maxByOrNull} from "./sequence/maxByOrNull"; +import {minOf} from "./sequence/minOf"; +import {minOfOrNull} from "./sequence/minOfOrNull"; +import {maxOf} from "./sequence/maxOf"; +import {maxOfOrNull} from "./sequence/maxOfOrNull"; +import {minWith} from "./sequence/minWith"; +import {minWithOrNull} from "./sequence/minWithOrNull"; +import {maxWith} from "./sequence/maxWith"; +import {maxWithOrNull} from "./sequence/maxWithOrNull"; +import {minus} from "./sequence/minus"; +import {reduce} from "./sequence/reduce"; +import {reduceOrNull} from "./sequence/reduceOrNull"; +import {fold} from "./sequence/fold"; +import {scan} from "./sequence/scan"; +import {partition} from "./sequence/partition"; +import {sortedWith} from "./sequence/sortedWith"; +import {sortedBy} from "./sequence/sortedBy"; +import {reversed} from "./sequence/reversed"; +import {windowed} from "./sequence/windowed"; +import {chunked} from "./sequence/chunked"; +import {zip} from "./sequence/zip"; +import {unzip} from "./sequence/unzip"; +import {sum} from "./sequence/sum"; +import {average} from "./sequence/average"; +import {min} from "./sequence/min"; +import {minOrNull} from "./sequence/minOrNull"; +import {max} from "./sequence/max"; +import {maxOrNull} from "./sequence/maxOrNull"; +import {flatten} from "./sequence/flatten"; +import {sorted} from "./sequence/sorted"; +import {onEach} from "./sequence/onEach"; +import {joinToString} from "./sequence/joinToString"; +import {groupBy} from "./sequence/groupBy"; +import {averageOrNull} from "./sequence/averageOrNull"; +import {averageByOrNull} from "./sequence/averageByOrNull"; +import {toSet} from "./sequence/toSet"; +import {toMap} from "./sequence/toMap"; +import {mapNotNull} from "./sequence/mapNotNull"; +import {intersect} from "./sequence/intersect"; export type Sequence = { [Symbol.iterator](): Iterator - collect(): T[] - onEach(action: (it: T) => void): void + toArray(): T[] + toSet(): Set + toMap(this: Sequence<[K, V]>): Map + + onEach(action: (it: T) => void): Sequence forEach(action: (it: T) => void): void isEmpty(): boolean isNotEmpty(): boolean count(predicate?: (it: T) => boolean): number - firstIndex(predicate?: (it: T) => boolean): number - firstIndexOrNull(predicate?: (it: T) => boolean): number | null - lastIndex(predicate?: (it: T) => boolean): number - lastIndexOrNull(predicate?: (it: T) => boolean): number | null indices(): Sequence withIndex(): Sequence<[number, T]> @@ -113,6 +111,17 @@ export type Sequence = { single(predicate?: (it: T) => boolean): T singleOrNull(predicate?: (it: T) => boolean): T | null + reduce(accumulate: (acc: T, it: T) => T): T + reduceOrNull(accumulate: (acc: T, it: T) => T): T | null + fold(initial: R, accumulate: (acc: R, it: T) => R): R + scan(initial: R, accumulate: (acc: R, it: T) => R): Sequence + sum(this: Sequence): number + sumBy(transform: (it: T) => number): number + average(this: Sequence): number + averageOrNull(this: Sequence): number | null + averageBy(transform: (it: T) => number): number + averageByOrNull(transform: (it: T) => number): number | null + min(this: Sequence): number minOrNull(this: Sequence): number | null minWith(compare: (a: T, b: T) => number): T @@ -121,6 +130,7 @@ export type Sequence = { minByOrNull(transform: (it: T) => number): T | null minOf(transform: (it: T) => number): number minOfOrNull(transform: (it: T) => number): number | null + max(this: Sequence): number maxOrNull(this: Sequence): number | null maxWith(compare: (a: T, b: T) => number): T @@ -130,23 +140,18 @@ export type Sequence = { maxOf(transform: (it: T) => number): number maxOfOrNull(transform: (it: T) => number): number | null - reduce(accumulate: (acc: T, it: T) => T): T - reduceOrNull(accumulate: (acc: T, it: T) => T): T | null - fold(initial: R, accumulate: (acc: R, it: T) => R): R - scan(initial: R, accumulate: (acc: R, it: T) => R): Sequence - sum(this: Sequence): number - sumBy(transform: (it: T) => number): number - average(this: Sequence): number - averageBy(transform: (it: T) => number): number + joinToString(separator: string, transform?: (it: T) => string): string plus(items: Iterable): Sequence minus(items: Iterable): Sequence + intersect(items: Iterable): Sequence filter(predicate: (it: T) => it is R): Sequence filter(predicate: (it: T) => boolean): Sequence - filterIndexed(predicate: (index: number, it: T) => it is R): Sequence - filterIndexed(predicate: (index: number, it: T) => boolean): Sequence - partition(predicate: (it: T) => boolean): [T[], T[]] + partition(predicate: (it: T) => boolean): [Sequence, Sequence] + distinct(): Sequence + distinctBy(transform: (it: T) => K): Sequence + drop(count: number): Sequence dropWhile(predicate: (it: T) => boolean): Sequence dropUntil(predicate: (it: T) => boolean): Sequence @@ -154,284 +159,279 @@ export type Sequence = { takeWhile(predicate: (it: T) => boolean): Sequence takeUntil(predicate: (it: T) => boolean): Sequence slice(start: number, end?: number): Sequence - distinct(): Sequence - distinctBy(transform: (it: T) => K): Sequence map(transform: (it: T) => R): Sequence - mapIndexed(transform: (index: number, it: T) => R): Sequence + mapNotNull(transform: (it: T) => R | null): Sequence flatten(this: Sequence>): Sequence flatMap(transform: (it: T) => Iterable): Sequence - flatMapIndexed(transform: (index: number, it: T) => Iterable): Sequence + + groupBy(transform: (it: T) => K): Sequence<[K, T[]]> associate(transform: (it: T) => [K, V]): Sequence<[K, V]> associateBy(transform: (it: T) => K): Sequence<[K, T]> associateWith(transform: (it: T) => V): Sequence<[T, V]> zip(items: Iterable): Sequence<[T, B]> - unzip(this: Sequence<[A, B]>): [A[], B[]] + unzip(this: Sequence<[A, B]>): [Sequence, Sequence] windowed(size: number, step?: number, partial?: boolean): Sequence chunked(size: number): Sequence - groupBy(transform: (it: T) => K): [K, T[]][] - - reversed(): T[] - sorted(this: Sequence): number[] - sortedWith(compare: (a: T, b: T) => number): T[] - sortedBy(transform: (it: T) => number): T[] - - joinToString(separator: string, transform?: (it: T) => string): string + reversed(): Sequence + sorted(this: Sequence): Sequence + sortedWith(compare: (a: T, b: T) => number): Sequence + sortedBy(transform: (it: T) => number): Sequence } export function sequence(value: Iterable): Sequence { return wrap(() => value); } -export function wrap(factory: () => Iterable): Sequence { +function wrap(value: Iterable | (() => Iterable)): Sequence { + const resolve = () => typeof value === "function" ? value() : value; return { [Symbol.iterator]() { - return factory()[Symbol.iterator](); + return resolve()[Symbol.iterator](); }, - collect() { - return collect(factory()); + toArray() { + return toArray(resolve()); + }, + toSet() { + return toSet(resolve()); + }, + toMap() { + return toMap(resolve() as Iterable<[K, V]>); }, onEach(action) { - return wrap(() => onEach(factory(), action)); + return wrap(() => onEach(resolve(), action)); }, forEach(action) { - forEach(factory(), action); + forEach(resolve(), action); }, isEmpty() { - return isEmpty(factory()); + return isEmpty(resolve()); }, isNotEmpty() { - return isNotEmpty(factory()); + return isNotEmpty(resolve()); }, count(predicate) { - return count(factory(), predicate); - }, - firstIndex(predicate) { - return firstIndex(factory(), predicate); - }, - firstIndexOrNull(predicate) { - return firstIndexOrNull(factory(), predicate); - }, - lastIndex(predicate) { - return lastIndex(factory(), predicate); - }, - lastIndexOrNull(predicate) { - return lastIndexOrNull(factory(), predicate); + return count(resolve(), predicate); }, indices() { - return wrap(() => indices(factory())); + return wrap(() => indices(resolve())); }, withIndex() { - return wrap(() => withIndex(factory())); + return wrap(() => withIndex(resolve())); }, contains(item) { - return contains(factory(), item); + return contains(resolve(), item); }, containsAll(items) { - return containsAll(factory(), items); + return containsAll(resolve(), items); }, any(predicate) { - return any(factory(), predicate); + return any(resolve(), predicate); }, all(predicate) { - return all(factory(), predicate); + return all(resolve(), predicate); }, none(predicate) { - return none(factory(), predicate); + return none(resolve(), predicate); }, get(index) { - return get(factory(), index); + return get(resolve(), index); }, getOrNull(index) { - return getOrNull(factory(), index); + return getOrNull(resolve(), index); }, first(predicate) { - return first(factory(), predicate); + return first(resolve(), predicate); }, firstOrNull(predicate) { - return firstOrNull(factory(), predicate); + return firstOrNull(resolve(), predicate); }, last(predicate) { - return last(factory(), predicate); + return last(resolve(), predicate); }, lastOrNull(predicate) { - return lastOrNull(factory(), predicate); + return lastOrNull(resolve(), predicate); }, single(predicate) { - return single(factory(), predicate); + return single(resolve(), predicate); }, singleOrNull(predicate) { - return singleOrNull(factory(), predicate); - }, - min() { - return min(factory() as any); - }, - minOrNull() { - return minOrNull(factory() as any); - }, - minWith(compare) { - return minWith(factory(), compare); - }, - minWithOrNull(compare) { - return minWithOrNull(factory(), compare); - }, - minBy(transform) { - return minBy(factory(), transform); - }, - minByOrNull(transform) { - return minByOrNull(factory(), transform); - }, - minOf(transform) { - return minOf(factory(), transform); - }, - minOfOrNull(transform) { - return minOfOrNull(factory(), transform); - }, - max() { - return max(factory() as any); - }, - maxOrNull() { - return maxOrNull(factory() as any); - }, - maxWith(compare) { - return maxWith(factory(), compare); - }, - maxWithOrNull(compare) { - return maxWithOrNull(factory(), compare); - }, - maxBy(transform) { - return maxBy(factory(), transform); - }, - maxByOrNull(transform) { - return maxByOrNull(factory(), transform); - }, - maxOf(transform) { - return maxOf(factory(), transform); - }, - maxOfOrNull(transform) { - return maxOfOrNull(factory(), transform); + return singleOrNull(resolve(), predicate); }, reduce(accumulate) { - return reduce(factory(), accumulate); + return reduce(resolve(), accumulate); }, reduceOrNull(accumulate) { - return reduceOrNull(factory(), accumulate); + return reduceOrNull(resolve(), accumulate); }, fold(initial, accumulate) { - return fold(factory(), initial, accumulate); + return fold(resolve(), initial, accumulate); }, scan(initial, accumulate) { - return wrap(() => scan(factory(), initial, accumulate)); + return wrap(() => scan(resolve(), initial, accumulate)); }, sum() { - return sum(factory() as any); + return sum(resolve() as Iterable); }, sumBy(transform) { - return sumBy(factory(), transform); + return sumBy(resolve(), transform); }, average() { - return average(factory() as any); + return average(resolve() as Iterable); + }, + averageOrNull() { + return averageOrNull(resolve() as Iterable); }, averageBy(transform) { - return averageBy(factory(), transform); + return averageBy(resolve(), transform); }, - plus(items) { - return wrap(() => plus(factory(), items)); + averageByOrNull(transform) { + return averageByOrNull(resolve(), transform); }, - minus(items) { - return wrap(() => minus(factory(), items)); + min() { + return min(resolve() as Iterable); }, - filter(predicate: (it: T) => boolean) { - return wrap(() => filter(factory(), predicate)); + minOrNull() { + return minOrNull(resolve() as Iterable); }, - filterIndexed(predicate: (index: number, it: T) => boolean) { - return wrap(() => filterIndexed(factory(), predicate)); + minWith(compare) { + return minWith(resolve(), compare); }, - partition(predicate) { - return partition(factory(), predicate); + minWithOrNull(compare) { + return minWithOrNull(resolve(), compare); }, - drop(count) { - return wrap(() => drop(factory(), count)); + minBy(transform) { + return minBy(resolve(), transform); }, - dropWhile(predicate) { - return wrap(() => dropWhile(factory(), predicate)); + minByOrNull(transform) { + return minByOrNull(resolve(), transform); }, - dropUntil(predicate) { - return wrap(() => dropUntil(factory(), predicate)); + minOf(transform) { + return minOf(resolve(), transform); }, - take(count) { - return wrap(() => take(factory(), count)); + minOfOrNull(transform) { + return minOfOrNull(resolve(), transform); }, - takeWhile(predicate) { - return wrap(() => takeWhile(factory(), predicate)); + max() { + return max(resolve() as Iterable); }, - takeUntil(predicate) { - return wrap(() => takeUntil(factory(), predicate)); + maxOrNull() { + return maxOrNull(resolve() as Iterable); }, - slice(start, end) { - return wrap(() => slice(factory(), start, end)); + maxWith(compare) { + return maxWith(resolve(), compare); }, - distinct() { - return wrap(() => distinct(factory())); + maxWithOrNull(compare) { + return maxWithOrNull(resolve(), compare); }, - distinctBy(transform) { - return wrap(() => distinctBy(factory(), transform)); + maxBy(transform) { + return maxBy(resolve(), transform); }, - map(transform) { - return wrap(() => map(factory(), transform)); + maxByOrNull(transform) { + return maxByOrNull(resolve(), transform); }, - mapIndexed(transform) { - return wrap(() => mapIndexed(factory(), transform)); + maxOf(transform) { + return maxOf(resolve(), transform); }, - flatten() { - return wrap(() => flatten(factory() as any)); - }, - flatMap(transform) { - return wrap(() => flatMap(factory(), transform)); - }, - flatMapIndexed(transform) { - return wrap(() => flatMapIndexed(factory(), transform)); - }, - associate(transform) { - return wrap(() => associate(factory(), transform)); - }, - associateBy(transform) { - return wrap(() => associateBy(factory(), transform)); - }, - associateWith(transform) { - return wrap(() => associateWith(factory(), transform)); - }, - zip(items) { - return wrap(() => zip(factory(), items)); - }, - unzip() { - return unzip(factory() as any); - }, - windowed(size, step, partial) { - return wrap(() => windowed(factory(), size, step, partial)); - }, - chunked(size) { - return wrap(() => chunked(factory(), size)); - }, - groupBy(transform) { - return groupBy(factory(), transform); - }, - reversed() { - return reversed(factory()); - }, - sorted() { - return sorted(factory() as any); - }, - sortedWith(compare) { - return sortedWith(factory(), compare); - }, - sortedBy(transform) { - return sortedBy(factory(), transform); + maxOfOrNull(transform) { + return maxOfOrNull(resolve(), transform); }, joinToString(separator, transform) { - return joinToString(factory(), separator, transform); + return joinToString(resolve(), separator, transform); + }, + plus(items) { + return wrap(() => plus(resolve(), items)); + }, + minus(items) { + return wrap(() => minus(resolve(), items)); + }, + intersect(items) { + return wrap(() => intersect(resolve(), items)); + }, + filter(predicate: (it: T) => boolean) { + return wrap(() => filter(resolve(), predicate)); + }, + partition(predicate) { + const [a, b] = partition(resolve(), predicate); + return [wrap(a), wrap(b)]; + }, + distinct() { + return wrap(() => distinct(resolve())); + }, + distinctBy(transform) { + return wrap(() => distinctBy(resolve(), transform)); + }, + drop(count) { + return wrap(() => drop(resolve(), count)); + }, + dropWhile(predicate) { + return wrap(() => dropWhile(resolve(), predicate)); + }, + dropUntil(predicate) { + return wrap(() => dropUntil(resolve(), predicate)); + }, + take(count) { + return wrap(() => take(resolve(), count)); + }, + takeWhile(predicate) { + return wrap(() => takeWhile(resolve(), predicate)); + }, + takeUntil(predicate) { + return wrap(() => takeUntil(resolve(), predicate)); + }, + slice(start, end) { + return wrap(() => slice(resolve(), start, end)); + }, + map(transform) { + return wrap(() => map(resolve(), transform)); + }, + mapNotNull(transform) { + return wrap(() => mapNotNull(resolve(), transform)); + }, + flatten() { + return wrap(() => flatten(resolve() as Iterable>)); + }, + flatMap(transform) { + return wrap(() => flatMap(resolve(), transform)); + }, + groupBy(transform) { + return wrap(groupBy(resolve(), transform)); + }, + associate(transform) { + return wrap(() => associate(resolve(), transform)); + }, + associateBy(transform) { + return wrap(() => associateBy(resolve(), transform)); + }, + associateWith(transform) { + return wrap(() => associateWith(resolve(), transform)); + }, + zip(items) { + return wrap(() => zip(resolve(), items)); + }, + unzip() { + const [a, b] = unzip(resolve() as Iterable<[A, B]>); + return [wrap(a), wrap(b)]; + }, + windowed(size, step, partial) { + return wrap(() => windowed(resolve(), size, step, partial)); + }, + chunked(size) { + return wrap(() => chunked(resolve(), size)); + }, + reversed() { + return wrap(reversed(resolve())); + }, + sorted() { + return wrap(sorted(resolve() as Iterable)); + }, + sortedWith(compare) { + return wrap(sortedWith(resolve(), compare)); + }, + sortedBy(transform) { + return wrap(sortedBy(resolve(), transform)); }, }; } diff --git a/src/sequence/average.ts b/src/sequence/average.ts index c35528f..fad8e09 100644 --- a/src/sequence/average.ts +++ b/src/sequence/average.ts @@ -1,3 +1,5 @@ +import {error} from "../util/error"; + export function average(value: Iterable): number { let count = 0; let sum = 0; @@ -5,5 +7,5 @@ export function average(value: Iterable): number { count++; sum += it; } - return count !== 0 ? sum / count : 0; + return count !== 0 ? sum / count : error(); } diff --git a/src/sequence/averageBy.ts b/src/sequence/averageBy.ts index 5325254..a97a28e 100644 --- a/src/sequence/averageBy.ts +++ b/src/sequence/averageBy.ts @@ -1,3 +1,5 @@ +import {error} from "../util/error"; + export function averageBy(value: Iterable, transform: (it: T) => number): number { let count = 0; let sum = 0; @@ -5,5 +7,5 @@ export function averageBy(value: Iterable, transform: (it: T) => number): count++; sum += transform(it); } - return count !== 0 ? sum / count : 0; + return count !== 0 ? sum / count : error(); } diff --git a/src/sequence/averageByOrNull.ts b/src/sequence/averageByOrNull.ts new file mode 100644 index 0000000..ed1b49c --- /dev/null +++ b/src/sequence/averageByOrNull.ts @@ -0,0 +1,9 @@ +export function averageByOrNull(value: Iterable, transform: (it: T) => number): number | null { + let count = 0; + let sum = 0; + for (const it of value) { + count++; + sum += transform(it); + } + return count !== 0 ? sum / count : null; +} diff --git a/src/sequence/averageOrNull.ts b/src/sequence/averageOrNull.ts new file mode 100644 index 0000000..c275ee3 --- /dev/null +++ b/src/sequence/averageOrNull.ts @@ -0,0 +1,9 @@ +export function averageOrNull(value: Iterable): number | null { + let count = 0; + let sum = 0; + for (const it of value) { + count++; + sum += it; + } + return count !== 0 ? sum / count : null; +} diff --git a/src/sequence/chunked.ts b/src/sequence/chunked.ts index f22fd9f..a47cb2b 100644 --- a/src/sequence/chunked.ts +++ b/src/sequence/chunked.ts @@ -1,4 +1,7 @@ +import {error} from "../util/error"; + export function* chunked(value: Iterable, size: number): Iterable { + if (size < 1) error(); let buffer: T[] = []; for (const it of value) { buffer.push(it); diff --git a/src/sequence/collect.ts b/src/sequence/collect.ts deleted file mode 100644 index bc52b36..0000000 --- a/src/sequence/collect.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function collect(value: Iterable): T[] { - return [...value]; -} diff --git a/src/sequence/containsAll.ts b/src/sequence/containsAll.ts index 9ae4595..6e36037 100644 --- a/src/sequence/containsAll.ts +++ b/src/sequence/containsAll.ts @@ -1,7 +1,7 @@ export function containsAll(value: Iterable, items: Iterable): boolean { - const set = new Set(items); - for (const it of value) { - set.delete(it); + const set = new Set(value); + for (const it of items) { + if (!set.has(it)) return false; } - return set.size === 0; + return true; } diff --git a/src/sequence/drop.ts b/src/sequence/drop.ts index c8a1567..4a227eb 100644 --- a/src/sequence/drop.ts +++ b/src/sequence/drop.ts @@ -1,7 +1,13 @@ +import {error} from "../util/error"; + export function* drop(value: Iterable, count: number): Iterable { + if (count < 0) error(); let dropped = 0; for (const it of value) { - if (dropped === count) yield it; - else dropped++; + if (dropped < count) { + dropped++; + continue; + } + yield it; } } diff --git a/src/sequence/dropUntil.ts b/src/sequence/dropUntil.ts index 305329c..79f42db 100644 --- a/src/sequence/dropUntil.ts +++ b/src/sequence/dropUntil.ts @@ -1,12 +1,11 @@ export function* dropUntil(value: Iterable, predicate: (it: T) => boolean): Iterable { const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - while (!done) { - if (predicate(it)) break; - ({value: it, done} = iterator.next()); + let next = iterator.next(); + while (!next.done && !predicate(next.value)) { + next = iterator.next(); } - while (!done) { - yield it; - ({value: it, done} = iterator.next()); + while (!next.done) { + yield next.value; + next = iterator.next(); } } diff --git a/src/sequence/dropWhile.ts b/src/sequence/dropWhile.ts index 94af9d2..e41e2f7 100644 --- a/src/sequence/dropWhile.ts +++ b/src/sequence/dropWhile.ts @@ -1,12 +1,11 @@ export function* dropWhile(value: Iterable, predicate: (it: T) => boolean): Iterable { const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - while (!done) { - if (!predicate(it)) break; - ({value: it, done} = iterator.next()); + let next = iterator.next(); + while (!next.done && predicate(next.value)) { + next = iterator.next(); } - while (!done) { - yield it; - ({value: it, done} = iterator.next()); + while (!next.done) { + yield next.value; + next = iterator.next(); } } diff --git a/src/sequence/filterIndexed.ts b/src/sequence/filterIndexed.ts deleted file mode 100644 index a5850b0..0000000 --- a/src/sequence/filterIndexed.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function* filterIndexed(value: Iterable, predicate: (index: number, it: T) => boolean): Iterable { - let index = 0; - for (const it of value) { - if (predicate(index++, it)) yield it; - } -} diff --git a/src/sequence/first.ts b/src/sequence/first.ts index dda1b8d..ae5c750 100644 --- a/src/sequence/first.ts +++ b/src/sequence/first.ts @@ -1,4 +1,4 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function first(value: Iterable, predicate?: (it: T) => boolean): T { for (const it of value) { diff --git a/src/sequence/firstIndex.ts b/src/sequence/firstIndex.ts deleted file mode 100644 index 32f1d9b..0000000 --- a/src/sequence/firstIndex.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {error} from "../util/error.js"; - -export function firstIndex(value: Iterable, predicate?: (it: T) => boolean): number { - let index = 0; - for (const it of value) { - if (!predicate || predicate(it)) return index; - index++; - } - error(); -} diff --git a/src/sequence/firstIndexOrNull.ts b/src/sequence/firstIndexOrNull.ts deleted file mode 100644 index 9e5b883..0000000 --- a/src/sequence/firstIndexOrNull.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function firstIndexOrNull(value: Iterable, predicate?: (it: T) => boolean): number | null { - let index = 0; - for (const it of value) { - if (!predicate || predicate(it)) return index; - index++; - } - return null; -} diff --git a/src/sequence/flatMapIndexed.ts b/src/sequence/flatMapIndexed.ts deleted file mode 100644 index cc573e7..0000000 --- a/src/sequence/flatMapIndexed.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function* flatMapIndexed(value: Iterable, transform: (index: number, it: T) => Iterable): Iterable { - let index = 0; - for (const it of value) { - yield* transform(index++, it); - } -} diff --git a/src/sequence/get.ts b/src/sequence/get.ts index 682ac3e..7bd8abd 100644 --- a/src/sequence/get.ts +++ b/src/sequence/get.ts @@ -1,6 +1,7 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function get(value: Iterable, index: number): T { + if (index < 0) error(); let i = 0; for (const it of value) { if (i++ === index) return it; diff --git a/src/sequence/getOrNull.ts b/src/sequence/getOrNull.ts index a2a017d..cd00e80 100644 --- a/src/sequence/getOrNull.ts +++ b/src/sequence/getOrNull.ts @@ -1,4 +1,5 @@ export function getOrNull(value: Iterable, index: number): T | null { + if (index < 0) return null; let i = 0; for (const it of value) { if (i++ === index) return it; diff --git a/src/sequence/indices.ts b/src/sequence/indices.ts index da86ccb..955ed39 100644 --- a/src/sequence/indices.ts +++ b/src/sequence/indices.ts @@ -1,4 +1,4 @@ -export function* indices(value: Iterable): Iterable { +export function* indices(value: Iterable): Iterable { let index = 0; for (const _ of value) { yield index++; diff --git a/src/sequence/intersect.ts b/src/sequence/intersect.ts new file mode 100644 index 0000000..2bc1f9f --- /dev/null +++ b/src/sequence/intersect.ts @@ -0,0 +1,6 @@ +export function* intersect(value: Iterable, items: Iterable): Iterable { + const set = new Set(items); + for (const it of value) { + if (set.has(it)) yield it; + } +} diff --git a/src/sequence/last.ts b/src/sequence/last.ts index 2077529..7dad2c8 100644 --- a/src/sequence/last.ts +++ b/src/sequence/last.ts @@ -1,13 +1,10 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function last(value: Iterable, predicate?: (it: T) => boolean): T { - let last!: T; - let found = false; + let last: T | undefined; for (const it of value) { - if (predicate && !predicate(it)) continue; - last = it; - found = true; + if (!predicate || predicate(it)) last = it; } - if (!found) error(); + if (last === undefined) error(); return last; } diff --git a/src/sequence/lastIndex.ts b/src/sequence/lastIndex.ts deleted file mode 100644 index a6c1dba..0000000 --- a/src/sequence/lastIndex.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {error} from "../util/error.js"; - -export function lastIndex(value: Iterable, predicate?: (it: T) => boolean): number { - let last: number | undefined; - let index = 0; - for (const it of value) { - if (!predicate || predicate(it)) last = index; - index++; - } - if (last === undefined) error(); - return last; -} diff --git a/src/sequence/lastIndexOrNull.ts b/src/sequence/lastIndexOrNull.ts deleted file mode 100644 index e9a44e6..0000000 --- a/src/sequence/lastIndexOrNull.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function lastIndexOrNull(value: Iterable, predicate?: (it: T) => boolean): number | null { - let last: number | undefined; - let index = 0; - for (const it of value) { - if (!predicate || predicate(it)) last = index; - index++; - } - if (last === undefined) return null; - return last; -} diff --git a/src/sequence/lastOrNull.ts b/src/sequence/lastOrNull.ts index 012bcb5..182666b 100644 --- a/src/sequence/lastOrNull.ts +++ b/src/sequence/lastOrNull.ts @@ -1,11 +1,8 @@ export function lastOrNull(value: Iterable, predicate?: (it: T) => boolean): T | null { - let last!: T; - let found = false; + let last: T | undefined; for (const it of value) { - if (predicate && !predicate(it)) continue; - last = it; - found = true; + if (!predicate || predicate(it)) last = it; } - if (!found) return null; + if (last === undefined) return null; return last; } diff --git a/src/sequence/mapIndexed.ts b/src/sequence/mapIndexed.ts deleted file mode 100644 index d9870f6..0000000 --- a/src/sequence/mapIndexed.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function* mapIndexed(value: Iterable, transform: (index: number, it: T) => R): Iterable { - let index = 0; - for (const it of value) { - yield transform(index++, it); - } -} diff --git a/src/sequence/mapNotNull.ts b/src/sequence/mapNotNull.ts new file mode 100644 index 0000000..c63f73e --- /dev/null +++ b/src/sequence/mapNotNull.ts @@ -0,0 +1,6 @@ +export function* mapNotNull(value: Iterable, transform: (it: T) => R | null): Iterable { + for (const it of value) { + const transformed = transform(it); + if (transformed !== null) yield transformed; + } +} diff --git a/src/sequence/max.ts b/src/sequence/max.ts index 103865b..46e53c4 100644 --- a/src/sequence/max.ts +++ b/src/sequence/max.ts @@ -1,13 +1,10 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function max(value: Iterable): number { - let max!: number; - let found = false; + let max: number | undefined; for (const it of value) { - if (found && it <= max) continue; - max = it; - found = true; + if (max === undefined || it > max) max = it; } - if (!found) error(); + if (max === undefined) error(); return max; } diff --git a/src/sequence/maxBy.ts b/src/sequence/maxBy.ts index e1ffa9b..ff3ffb4 100644 --- a/src/sequence/maxBy.ts +++ b/src/sequence/maxBy.ts @@ -1,16 +1,15 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function maxBy(value: Iterable, transform: (it: T) => number): T { - let maxIt!: T; - let max!: number; - let found = false; + let max!: T; + let maxVal: number | undefined; for (const it of value) { const val = transform(it); - if (found && val <= max) continue; - maxIt = it; - max = val; - found = true; + if (maxVal === undefined || val > maxVal) { + max = it; + maxVal = val; + } } - if (!found) error(); - return maxIt; + if (maxVal === undefined) error(); + return max; } diff --git a/src/sequence/maxByOrNull.ts b/src/sequence/maxByOrNull.ts index 33aa045..74df039 100644 --- a/src/sequence/maxByOrNull.ts +++ b/src/sequence/maxByOrNull.ts @@ -1,14 +1,13 @@ export function maxByOrNull(value: Iterable, transform: (it: T) => number): T | null { - let maxIt!: T; - let max!: number; - let found = false; + let max!: T; + let maxVal: number | undefined; for (const it of value) { const val = transform(it); - if (found && val <= max) continue; - maxIt = it; - max = val; - found = true; + if (maxVal === undefined || val > maxVal) { + max = it; + maxVal = val; + } } - if (!found) return null; - return maxIt; + if (maxVal === undefined) return null; + return max; } diff --git a/src/sequence/maxOf.ts b/src/sequence/maxOf.ts index c7dd585..2ef736f 100644 --- a/src/sequence/maxOf.ts +++ b/src/sequence/maxOf.ts @@ -1,14 +1,11 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function maxOf(value: Iterable, transform: (it: T) => number): number { - let max!: number; - let found = false; + let max: number | undefined; for (const it of value) { const val = transform(it); - if (found && val <= max) continue; - max = val; - found = true; + if (max === undefined || val > max) max = val; } - if (!found) error(); + if (max === undefined) error(); return max; } diff --git a/src/sequence/maxOfOrNull.ts b/src/sequence/maxOfOrNull.ts index 49f165a..e1f80cc 100644 --- a/src/sequence/maxOfOrNull.ts +++ b/src/sequence/maxOfOrNull.ts @@ -1,12 +1,9 @@ export function maxOfOrNull(value: Iterable, transform: (it: T) => number): number | null { - let max!: number; - let found = false; + let max: number | undefined; for (const it of value) { const val = transform(it); - if (found && val <= max) continue; - max = val; - found = true; + if (max === undefined || val > max) max = val; } - if (!found) return null; + if (max === undefined) return null; return max; } diff --git a/src/sequence/maxOrNull.ts b/src/sequence/maxOrNull.ts index fc8b767..d86e713 100644 --- a/src/sequence/maxOrNull.ts +++ b/src/sequence/maxOrNull.ts @@ -1,11 +1,8 @@ export function maxOrNull(value: Iterable): number | null { - let max!: number; - let found = false; + let max: number | undefined; for (const it of value) { - if (found && it <= max) continue; - max = it; - found = true; + if (max === undefined || it > max) max = it; } - if (!found) return null; + if (max === undefined) return null; return max; } diff --git a/src/sequence/maxWith.ts b/src/sequence/maxWith.ts index af9cda8..8f64e87 100644 --- a/src/sequence/maxWith.ts +++ b/src/sequence/maxWith.ts @@ -1,13 +1,10 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function maxWith(value: Iterable, compare: (a: T, b: T) => number): T { - let max!: T; - let found = false; + let max: T | undefined; for (const it of value) { - if (found && compare(it, max) <= 0) continue; - max = it; - found = true; + if (max === undefined || compare(it, max) > 0) max = it; } - if (!found) error(); + if (max === undefined) error(); return max; } diff --git a/src/sequence/maxWithOrNull.ts b/src/sequence/maxWithOrNull.ts index 6182181..090e31f 100644 --- a/src/sequence/maxWithOrNull.ts +++ b/src/sequence/maxWithOrNull.ts @@ -1,11 +1,8 @@ export function maxWithOrNull(value: Iterable, compare: (a: T, b: T) => number): T | null { - let max!: T; - let found = false; + let max: T | undefined; for (const it of value) { - if (found && compare(it, max) <= 0) continue; - max = it; - found = true; + if (max === undefined || compare(it, max) > 0) max = it; } - if (!found) return null; + if (max === undefined) return null; return max; } diff --git a/src/sequence/min.ts b/src/sequence/min.ts index eebc0fc..4cbd677 100644 --- a/src/sequence/min.ts +++ b/src/sequence/min.ts @@ -1,13 +1,10 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function min(value: Iterable): number { - let min!: number; - let found = false; + let min: number | undefined; for (const it of value) { - if (found && it >= min) continue; - min = it; - found = true; + if (min === undefined || it < min) min = it; } - if (!found) error(); + if (min === undefined) error(); return min; } diff --git a/src/sequence/minBy.ts b/src/sequence/minBy.ts index 5b0be99..66d5e68 100644 --- a/src/sequence/minBy.ts +++ b/src/sequence/minBy.ts @@ -1,16 +1,15 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function minBy(value: Iterable, transform: (it: T) => number): T { let minIt!: T; - let min!: number; - let found = false; + let minVal: number | undefined; for (const it of value) { const val = transform(it); - if (found && val >= min) continue; - minIt = it; - min = val; - found = true; + if (minVal === undefined || val < minVal) { + minIt = it; + minVal = val; + } } - if (!found) error(); + if (minVal === undefined) error(); return minIt; } diff --git a/src/sequence/minByOrNull.ts b/src/sequence/minByOrNull.ts index e220586..748c63e 100644 --- a/src/sequence/minByOrNull.ts +++ b/src/sequence/minByOrNull.ts @@ -1,14 +1,13 @@ export function minByOrNull(value: Iterable, transform: (it: T) => number): T | null { let minIt!: T; - let min!: number; - let found = false; + let minVal: number | undefined; for (const it of value) { const val = transform(it); - if (found && val >= min) continue; - minIt = it; - min = val; - found = true; + if (minVal === undefined || val < minVal) { + minIt = it; + minVal = val; + } } - if (!found) return null; + if (minVal === undefined) return null; return minIt; } diff --git a/src/sequence/minOf.ts b/src/sequence/minOf.ts index 24bd182..9692eb4 100644 --- a/src/sequence/minOf.ts +++ b/src/sequence/minOf.ts @@ -1,14 +1,11 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function minOf(value: Iterable, transform: (it: T) => number): number { - let min!: number; - let found = false; + let min: number | undefined; for (const it of value) { const val = transform(it); - if (found && val >= min) continue; - min = val; - found = true; + if (min === undefined || val < min) min = val; } - if (!found) error(); + if (min === undefined) error(); return min; } diff --git a/src/sequence/minOfOrNull.ts b/src/sequence/minOfOrNull.ts index afbd75a..0224e9d 100644 --- a/src/sequence/minOfOrNull.ts +++ b/src/sequence/minOfOrNull.ts @@ -1,12 +1,9 @@ export function minOfOrNull(value: Iterable, transform: (it: T) => number): number | null { - let min!: number; - let found = false; + let min: number | undefined; for (const it of value) { const val = transform(it); - if (found && val >= min) continue; - min = val; - found = true; + if (min === undefined || val < min) min = val; } - if (!found) return null; + if (min === undefined) return null; return min; } diff --git a/src/sequence/minOrNull.ts b/src/sequence/minOrNull.ts index 906e955..dc9a1b0 100644 --- a/src/sequence/minOrNull.ts +++ b/src/sequence/minOrNull.ts @@ -1,11 +1,8 @@ export function minOrNull(value: Iterable): number | null { - let min!: number; - let found = false; + let min: number | undefined; for (const it of value) { - if (found && it <= min) continue; - min = it; - found = true; + if (min === undefined || it < min) min = it; } - if (!found) return null; + if (min === undefined) return null; return min; } diff --git a/src/sequence/minWith.ts b/src/sequence/minWith.ts index d2904fd..b773f6f 100644 --- a/src/sequence/minWith.ts +++ b/src/sequence/minWith.ts @@ -1,14 +1,10 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function minWith(value: Iterable, compare: (a: T, b: T) => number): T { - const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - if (done) error(); - let min = it; - ({value: it, done} = iterator.next()); - while (!done) { - if (compare(it, min) < 0) min = it; - ({value: it, done} = iterator.next()); + let min: T | undefined; + for (const it of value) { + if (min === undefined || compare(it, min) < 0) min = it; } + if (min === undefined) error(); return min; } diff --git a/src/sequence/minWithOrNull.ts b/src/sequence/minWithOrNull.ts index 5e11acc..2470d71 100644 --- a/src/sequence/minWithOrNull.ts +++ b/src/sequence/minWithOrNull.ts @@ -1,12 +1,8 @@ export function minWithOrNull(value: Iterable, compare: (a: T, b: T) => number): T | null { - const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - if (done) return null; - let min = it; - ({value: it, done} = iterator.next()); - while (!done) { - if (compare(it, min) < 0) min = it; - ({value: it, done} = iterator.next()); + let min: T | undefined; + for (const it of value) { + if (min === undefined || compare(it, min) < 0) min = it; } + if (min === undefined) return null; return min; } diff --git a/src/sequence/partition.ts b/src/sequence/partition.ts index 7ee920b..e4fb43e 100644 --- a/src/sequence/partition.ts +++ b/src/sequence/partition.ts @@ -1,9 +1,30 @@ -export function partition(value: Iterable, predicate: (it: T) => boolean): [T[], T[]] { +export function partition(value: Iterable, predicate: (it: T) => boolean): [() => Iterable, () => Iterable] { + const iterator = value[Symbol.iterator](); + let finished = false; const t: T[] = []; const f: T[] = []; - for (const it of value) { - if (predicate(it)) t.push(it); - else f.push(it); + + function next() { + const {value, done} = iterator.next(); + if (done) finished = true; + else (predicate(value) ? t : f).push(value); } - return [t, f]; + + function* T(): Iterable { + let i = 0; + while (!finished || i < t.length) { + if (i < t.length) yield t[i++]; + else next(); + } + } + + function* F(): Iterable { + let i = 0; + while (!finished || i < f.length) { + if (i < f.length) yield f[i++]; + else next(); + } + } + + return [T, F]; } diff --git a/src/sequence/plus.ts b/src/sequence/plus.ts index ba320c1..78fab7b 100644 --- a/src/sequence/plus.ts +++ b/src/sequence/plus.ts @@ -1,8 +1,8 @@ -export function* plus(value1: Iterable, value2: Iterable): Iterable { - for (const it of value1) { +export function* plus(value: Iterable, items: Iterable): Iterable { + for (const it of value) { yield it; } - for (const it of value2) { + for (const it of items) { yield it; } } diff --git a/src/sequence/reduce.ts b/src/sequence/reduce.ts index e19a97d..c362281 100644 --- a/src/sequence/reduce.ts +++ b/src/sequence/reduce.ts @@ -1,14 +1,14 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function reduce(value: Iterable, accumulate: (acc: T, it: T) => T): T { const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - if (done) error(); - let acc = it - ({value: it, done} = iterator.next()); - while (!done) { - acc = accumulate(acc, it); - ({value: it, done} = iterator.next()); + const first = iterator.next(); + if (first.done) error(); + let acc = first.value; + let next = iterator.next(); + while (!next.done) { + acc = accumulate(acc, next.value); + next = iterator.next(); } return acc; } diff --git a/src/sequence/reduceOrNull.ts b/src/sequence/reduceOrNull.ts index 8455f3f..aa75461 100644 --- a/src/sequence/reduceOrNull.ts +++ b/src/sequence/reduceOrNull.ts @@ -1,12 +1,12 @@ export function reduceOrNull(value: Iterable, accumulate: (acc: T, it: T) => T): T | null { const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - if (done) return null; - let acc = it - ({value: it, done} = iterator.next()); - while (!done) { - acc = accumulate(acc, it); - ({value: it, done} = iterator.next()); + const first = iterator.next(); + if (first.done) return null; + let acc = first.value; + let next = iterator.next(); + while (!next.done) { + acc = accumulate(acc, next.value); + next = iterator.next(); } return acc; } diff --git a/src/sequence/reversed.ts b/src/sequence/reversed.ts index 2a1eb88..6419568 100644 --- a/src/sequence/reversed.ts +++ b/src/sequence/reversed.ts @@ -1,3 +1,3 @@ -export function reversed(iterable: Iterable): T[] { - return [...iterable].reverse(); +export function reversed(value: Iterable): T[] { + return [...value].reverse(); } diff --git a/src/sequence/single.ts b/src/sequence/single.ts index 9db0d4f..3f2cc20 100644 --- a/src/sequence/single.ts +++ b/src/sequence/single.ts @@ -1,14 +1,13 @@ -import {error} from "../util/error.js"; +import {error} from "../util/error"; export function single(value: Iterable, predicate?: (it: T) => boolean): T { - let last!: T; - let found = false; + let last: T | undefined; for (const it of value) { - if (predicate && !predicate(it)) continue; - if (found) error(); - last = it; - found = true; + if (!predicate || predicate(it)) { + if (last !== undefined) error(); + last = it; + } } - if (!found) error(); + if (last === undefined) error(); return last; } diff --git a/src/sequence/singleOrNull.ts b/src/sequence/singleOrNull.ts index 417b830..ad3d074 100644 --- a/src/sequence/singleOrNull.ts +++ b/src/sequence/singleOrNull.ts @@ -1,12 +1,11 @@ export function singleOrNull(value: Iterable, predicate?: (it: T) => boolean): T | null { - let last!: T; - let found = false; + let last: T | undefined; for (const it of value) { - if (predicate && !predicate(it)) continue; - if (found) return null; - last = it; - found = true; + if (!predicate || predicate(it)) { + if (last !== undefined) return null; + last = it; + } } - if (!found) return null; + if (last === undefined) return null; return last; } diff --git a/src/sequence/slice.ts b/src/sequence/slice.ts index 9cf7144..32cddf1 100644 --- a/src/sequence/slice.ts +++ b/src/sequence/slice.ts @@ -1,7 +1,16 @@ -export function* slice(value: Iterable, start: number, endInclusive?: number): Iterable { +import {error} from "../util/error"; + +export function* slice(value: Iterable, start: number, end?: number): Iterable { + if (start < 0) error(); + if (end !== undefined) { + if (end < 0) error(); + if (start > end) error(); + } let index = 0; for (const it of value) { - if (endInclusive !== undefined && index > endInclusive) return; - if (index++ >= start) yield it; + if (index >= start) yield it; + index++; + if (index === end) return; } + if (end !== undefined) error(); } diff --git a/src/sequence/sorted.ts b/src/sequence/sorted.ts index ea0909e..83bcd26 100644 --- a/src/sequence/sorted.ts +++ b/src/sequence/sorted.ts @@ -1,3 +1,3 @@ -export function sorted(iterable: Iterable): number[] { - return [...iterable].sort((a, b) => a - b); +export function sorted(value: Iterable): number[] { + return [...value].sort((a, b) => a - b); } diff --git a/src/sequence/sortedBy.ts b/src/sequence/sortedBy.ts index adfa7fc..ca75f84 100644 --- a/src/sequence/sortedBy.ts +++ b/src/sequence/sortedBy.ts @@ -1,3 +1,3 @@ -export function sortedBy(iterable: Iterable, transform: (it: T) => number): T[] { - return [...iterable].sort((a, b) => transform(a) - transform(b)); +export function sortedBy(value: Iterable, transform: (it: T) => number): T[] { + return [...value].sort((a, b) => transform(a) - transform(b)); } diff --git a/src/sequence/sortedWith.ts b/src/sequence/sortedWith.ts index 5ffff0b..8b500ea 100644 --- a/src/sequence/sortedWith.ts +++ b/src/sequence/sortedWith.ts @@ -1,3 +1,3 @@ -export function sortedWith(iterable: Iterable, compare: (a: T, b: T) => number): T[] { - return [...iterable].sort(compare); +export function sortedWith(value: Iterable, compare: (a: T, b: T) => number): T[] { + return [...value].sort(compare); } diff --git a/src/sequence/take.ts b/src/sequence/take.ts index 0ab257f..57df9c2 100644 --- a/src/sequence/take.ts +++ b/src/sequence/take.ts @@ -1,7 +1,11 @@ +import {error} from "../util/error"; + export function* take(value: Iterable, count: number): Iterable { + if (count < 0) error(); let taken = 0; for (const it of value) { - if (taken++ === count) return; yield it; + taken++; + if (taken === count) return; } } diff --git a/src/sequence/toArray.ts b/src/sequence/toArray.ts new file mode 100644 index 0000000..c20e572 --- /dev/null +++ b/src/sequence/toArray.ts @@ -0,0 +1,3 @@ +export function toArray(value: Iterable): T[] { + return [...value]; +} diff --git a/src/sequence/toMap.ts b/src/sequence/toMap.ts new file mode 100644 index 0000000..700c399 --- /dev/null +++ b/src/sequence/toMap.ts @@ -0,0 +1,3 @@ +export function toMap(value: Iterable<[K, V]>): Map { + return new Map(value); +} diff --git a/src/sequence/toSet.ts b/src/sequence/toSet.ts new file mode 100644 index 0000000..7d47a5d --- /dev/null +++ b/src/sequence/toSet.ts @@ -0,0 +1,3 @@ +export function toSet(value: Iterable): Set { + return new Set(value); +} diff --git a/src/sequence/unzip.ts b/src/sequence/unzip.ts index 2b9cb2f..25f6791 100644 --- a/src/sequence/unzip.ts +++ b/src/sequence/unzip.ts @@ -1,12 +1,29 @@ -export function unzip(value: Iterable<[A, B]>): [A[], B[]] { - const a: A[] = []; - const b: B[] = []; +export function unzip(value: Iterable<[A, B]>): [() => Iterable, () => Iterable] { const iterator = value[Symbol.iterator](); - let {value: it, done} = iterator.next(); - while (!done) { - a.push(it[0]); - b.push(it[1]); - ({value: it, done} = iterator.next()); + let finished = false; + const cache: [A, B][] = []; + + function next() { + const {value, done} = iterator.next(); + if (done) finished = true; + else cache.push(value); } - return [a, b]; + + function* A(): Iterable { + let i = 0; + while (!finished || i < cache.length) { + if (i < cache.length) yield cache[i++][0]; + else next(); + } + } + + function* B(): Iterable { + let i = 0; + while (!finished || i < cache.length) { + if (i < cache.length) yield cache[i++][1]; + else next(); + } + } + + return [A, B]; } diff --git a/src/sequence/windowed.ts b/src/sequence/windowed.ts index a6cf0c7..7d52a78 100644 --- a/src/sequence/windowed.ts +++ b/src/sequence/windowed.ts @@ -1,4 +1,8 @@ +import {error} from "../util/error"; + export function* windowed(value: Iterable, size: number, step: number = 1, partial: boolean = false): Iterable { + if (size < 1) error(); + if (step < 1) error(); const buffer: T[] = []; for (const it of value) { buffer.push(it); @@ -7,9 +11,10 @@ export function* windowed(value: Iterable, size: number, step: number = 1, buffer.splice(0, step); } } - if (!partial) return; - while (buffer.length !== 0) { - yield [...buffer]; - buffer.splice(0, step); + if (partial) { + while (buffer.length !== 0) { + yield [...buffer]; + buffer.splice(0, step); + } } } diff --git a/src/sequence/zip.ts b/src/sequence/zip.ts index 9d80876..3e470ea 100644 --- a/src/sequence/zip.ts +++ b/src/sequence/zip.ts @@ -1,9 +1,11 @@ export function* zip(value: Iterable, items: Iterable): Iterable<[T, B]> { const iteratorA = value[Symbol.iterator](); const iteratorB = items[Symbol.iterator](); - let {value: itA, done: doneA} = iteratorA.next(); - let {value: itB, done: doneB} = iteratorB.next(); - while (!doneA && !doneB) { - yield [itA, itB]; + let a = iteratorA.next(); + let b = iteratorB.next(); + while (!a.done && !b.done) { + yield [a.value, b.value]; + a = iteratorA.next(); + b = iteratorB.next(); } } diff --git a/tsconfig.json b/tsconfig.json index 166eeca..d7d58ee 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,13 @@ { "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", "target": "ESNext", - "rootDir": "./src/", - "outDir": "./dist/", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, "declaration": true, - "strict": true - } + "outDir": "./dist/" + }, + "include": [ + "./src/" + ] }