Implement sequence

This commit is contained in:
2026-04-25 20:55:50 +02:00
parent e2a18101cf
commit a5866c08ff
62 changed files with 570 additions and 569 deletions

View File

@@ -1 +1 @@
export {sequence} from "./sequence.js"; export {sequence} from "./sequence";

View File

@@ -1,100 +1,98 @@
import {map} from "./sequence/map.js"; import {map} from "./sequence/map";
import {collect} from "./sequence/collect.js"; import {toArray} from "./sequence/toArray";
import {forEach} from "./sequence/forEach.js"; import {forEach} from "./sequence/forEach";
import {filter} from "./sequence/filter.js"; import {filter} from "./sequence/filter";
import {count} from "./sequence/count.js"; import {count} from "./sequence/count";
import {isEmpty} from "./sequence/isEmpty.js"; import {isEmpty} from "./sequence/isEmpty";
import {isNotEmpty} from "./sequence/isNotEmpty.js"; import {isNotEmpty} from "./sequence/isNotEmpty";
import {indices} from "./sequence/indices.js"; import {indices} from "./sequence/indices";
import {withIndex} from "./sequence/withIndex.js"; import {withIndex} from "./sequence/withIndex";
import {filterIndexed} from "./sequence/filterIndexed.js"; import {any} from "./sequence/any";
import {mapIndexed} from "./sequence/mapIndexed.js"; import {none} from "./sequence/none";
import {any} from "./sequence/any.js"; import {all} from "./sequence/all";
import {none} from "./sequence/none.js"; import {first} from "./sequence/first";
import {all} from "./sequence/all.js"; import {firstOrNull} from "./sequence/firstOrNull";
import {first} from "./sequence/first.js"; import {last} from "./sequence/last";
import {firstOrNull} from "./sequence/firstOrNull.js"; import {lastOrNull} from "./sequence/lastOrNull";
import {last} from "./sequence/last.js"; import {single} from "./sequence/single";
import {lastOrNull} from "./sequence/lastOrNull.js"; import {singleOrNull} from "./sequence/singleOrNull";
import {single} from "./sequence/single.js"; import {plus} from "./sequence/plus";
import {singleOrNull} from "./sequence/singleOrNull.js"; import {distinct} from "./sequence/distinct";
import {plus} from "./sequence/plus.js"; import {distinctBy} from "./sequence/distinctBy";
import {distinct} from "./sequence/distinct.js"; import {drop} from "./sequence/drop";
import {distinctBy} from "./sequence/distinctBy.js"; import {take} from "./sequence/take";
import {drop} from "./sequence/drop.js"; import {slice} from "./sequence/slice";
import {take} from "./sequence/take.js"; import {dropWhile} from "./sequence/dropWhile";
import {slice} from "./sequence/slice.js"; import {dropUntil} from "./sequence/dropUntil";
import {dropWhile} from "./sequence/dropWhile.js"; import {takeWhile} from "./sequence/takeWhile";
import {dropUntil} from "./sequence/dropUntil.js"; import {takeUntil} from "./sequence/takeUntil";
import {takeWhile} from "./sequence/takeWhile.js"; import {flatMap} from "./sequence/flatMap";
import {takeUntil} from "./sequence/takeUntil.js"; import {associate} from "./sequence/associate";
import {flatMap} from "./sequence/flatMap.js"; import {associateBy} from "./sequence/associateBy";
import {flatMapIndexed} from "./sequence/flatMapIndexed.js"; import {associateWith} from "./sequence/associateWith";
import {associate} from "./sequence/associate.js"; import {get} from "./sequence/get";
import {associateBy} from "./sequence/associateBy.js"; import {getOrNull} from "./sequence/getOrNull";
import {associateWith} from "./sequence/associateWith.js"; import {sumBy} from "./sequence/sumBy";
import {get} from "./sequence/get.js"; import {averageBy} from "./sequence/averageBy";
import {getOrNull} from "./sequence/getOrNull.js"; import {contains} from "./sequence/contains";
import {sumBy} from "./sequence/sumBy.js"; import {containsAll} from "./sequence/containsAll";
import {averageBy} from "./sequence/averageBy.js"; import {minBy} from "./sequence/minBy";
import {contains} from "./sequence/contains.js"; import {minByOrNull} from "./sequence/minByOrNull";
import {containsAll} from "./sequence/containsAll.js"; import {maxBy} from "./sequence/maxBy";
import {minBy} from "./sequence/minBy.js"; import {maxByOrNull} from "./sequence/maxByOrNull";
import {minByOrNull} from "./sequence/minByOrNull.js"; import {minOf} from "./sequence/minOf";
import {maxBy} from "./sequence/maxBy.js"; import {minOfOrNull} from "./sequence/minOfOrNull";
import {maxByOrNull} from "./sequence/maxByOrNull.js"; import {maxOf} from "./sequence/maxOf";
import {minOf} from "./sequence/minOf.js"; import {maxOfOrNull} from "./sequence/maxOfOrNull";
import {minOfOrNull} from "./sequence/minOfOrNull.js"; import {minWith} from "./sequence/minWith";
import {maxOf} from "./sequence/maxOf.js"; import {minWithOrNull} from "./sequence/minWithOrNull";
import {maxOfOrNull} from "./sequence/maxOfOrNull.js"; import {maxWith} from "./sequence/maxWith";
import {minWith} from "./sequence/minWith.js"; import {maxWithOrNull} from "./sequence/maxWithOrNull";
import {minWithOrNull} from "./sequence/minWithOrNull.js"; import {minus} from "./sequence/minus";
import {maxWith} from "./sequence/maxWith.js"; import {reduce} from "./sequence/reduce";
import {maxWithOrNull} from "./sequence/maxWithOrNull.js"; import {reduceOrNull} from "./sequence/reduceOrNull";
import {minus} from "./sequence/minus.js"; import {fold} from "./sequence/fold";
import {reduce} from "./sequence/reduce.js"; import {scan} from "./sequence/scan";
import {reduceOrNull} from "./sequence/reduceOrNull.js"; import {partition} from "./sequence/partition";
import {fold} from "./sequence/fold.js"; import {sortedWith} from "./sequence/sortedWith";
import {scan} from "./sequence/scan.js"; import {sortedBy} from "./sequence/sortedBy";
import {partition} from "./sequence/partition.js"; import {reversed} from "./sequence/reversed";
import {firstIndex} from "./sequence/firstIndex.js"; import {windowed} from "./sequence/windowed";
import {firstIndexOrNull} from "./sequence/firstIndexOrNull.js"; import {chunked} from "./sequence/chunked";
import {lastIndex} from "./sequence/lastIndex.js"; import {zip} from "./sequence/zip";
import {lastIndexOrNull} from "./sequence/lastIndexOrNull.js"; import {unzip} from "./sequence/unzip";
import {sortedWith} from "./sequence/sortedWith.js"; import {sum} from "./sequence/sum";
import {sortedBy} from "./sequence/sortedBy.js"; import {average} from "./sequence/average";
import {reversed} from "./sequence/reversed.js"; import {min} from "./sequence/min";
import {windowed} from "./sequence/windowed.js"; import {minOrNull} from "./sequence/minOrNull";
import {chunked} from "./sequence/chunked.js"; import {max} from "./sequence/max";
import {zip} from "./sequence/zip.js"; import {maxOrNull} from "./sequence/maxOrNull";
import {unzip} from "./sequence/unzip.js"; import {flatten} from "./sequence/flatten";
import {sum} from "./sequence/sum.js"; import {sorted} from "./sequence/sorted";
import {average} from "./sequence/average.js"; import {onEach} from "./sequence/onEach";
import {min} from "./sequence/min.js"; import {joinToString} from "./sequence/joinToString";
import {minOrNull} from "./sequence/minOrNull.js"; import {groupBy} from "./sequence/groupBy";
import {max} from "./sequence/max.js"; import {averageOrNull} from "./sequence/averageOrNull";
import {maxOrNull} from "./sequence/maxOrNull.js"; import {averageByOrNull} from "./sequence/averageByOrNull";
import {flatten} from "./sequence/flatten.js"; import {toSet} from "./sequence/toSet";
import {sorted} from "./sequence/sorted.js"; import {toMap} from "./sequence/toMap";
import {onEach} from "./sequence/onEach.js"; import {mapNotNull} from "./sequence/mapNotNull";
import {joinToString} from "./sequence/joinToString.js"; import {intersect} from "./sequence/intersect";
import {groupBy} from "./sequence/groupBy.js";
export type Sequence<T> = { export type Sequence<T> = {
[Symbol.iterator](): Iterator<T> [Symbol.iterator](): Iterator<T>
collect(): T[]
onEach(action: (it: T) => void): void toArray(): T[]
toSet(): Set<T>
toMap<K, V>(this: Sequence<[K, V]>): Map<K, V>
onEach(action: (it: T) => void): Sequence<T>
forEach(action: (it: T) => void): void forEach(action: (it: T) => void): void
isEmpty(): boolean isEmpty(): boolean
isNotEmpty(): boolean isNotEmpty(): boolean
count(predicate?: (it: T) => boolean): number 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<number> indices(): Sequence<number>
withIndex(): Sequence<[number, T]> withIndex(): Sequence<[number, T]>
@@ -113,6 +111,17 @@ export type Sequence<T> = {
single(predicate?: (it: T) => boolean): T single(predicate?: (it: T) => boolean): T
singleOrNull(predicate?: (it: T) => boolean): T | null 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<R>(initial: R, accumulate: (acc: R, it: T) => R): R
scan<R>(initial: R, accumulate: (acc: R, it: T) => R): Sequence<R>
sum(this: Sequence<number>): number
sumBy(transform: (it: T) => number): number
average(this: Sequence<number>): number
averageOrNull(this: Sequence<number>): number | null
averageBy(transform: (it: T) => number): number
averageByOrNull(transform: (it: T) => number): number | null
min(this: Sequence<number>): number min(this: Sequence<number>): number
minOrNull(this: Sequence<number>): number | null minOrNull(this: Sequence<number>): number | null
minWith(compare: (a: T, b: T) => number): T minWith(compare: (a: T, b: T) => number): T
@@ -121,6 +130,7 @@ export type Sequence<T> = {
minByOrNull(transform: (it: T) => number): T | null minByOrNull(transform: (it: T) => number): T | null
minOf(transform: (it: T) => number): number minOf(transform: (it: T) => number): number
minOfOrNull(transform: (it: T) => number): number | null minOfOrNull(transform: (it: T) => number): number | null
max(this: Sequence<number>): number max(this: Sequence<number>): number
maxOrNull(this: Sequence<number>): number | null maxOrNull(this: Sequence<number>): number | null
maxWith(compare: (a: T, b: T) => number): T maxWith(compare: (a: T, b: T) => number): T
@@ -130,23 +140,18 @@ export type Sequence<T> = {
maxOf(transform: (it: T) => number): number maxOf(transform: (it: T) => number): number
maxOfOrNull(transform: (it: T) => number): number | null maxOfOrNull(transform: (it: T) => number): number | null
reduce(accumulate: (acc: T, it: T) => T): T joinToString(separator: string, transform?: (it: T) => string): string
reduceOrNull(accumulate: (acc: T, it: T) => T): T | null
fold<R>(initial: R, accumulate: (acc: R, it: T) => R): R
scan<R>(initial: R, accumulate: (acc: R, it: T) => R): Sequence<R>
sum(this: Sequence<number>): number
sumBy(transform: (it: T) => number): number
average(this: Sequence<number>): number
averageBy(transform: (it: T) => number): number
plus(items: Iterable<T>): Sequence<T> plus(items: Iterable<T>): Sequence<T>
minus(items: Iterable<T>): Sequence<T> minus(items: Iterable<T>): Sequence<T>
intersect(items: Iterable<T>): Sequence<T>
filter<R extends T>(predicate: (it: T) => it is R): Sequence<R> filter<R extends T>(predicate: (it: T) => it is R): Sequence<R>
filter(predicate: (it: T) => boolean): Sequence<T> filter(predicate: (it: T) => boolean): Sequence<T>
filterIndexed<R extends T>(predicate: (index: number, it: T) => it is R): Sequence<R> partition(predicate: (it: T) => boolean): [Sequence<T>, Sequence<T>]
filterIndexed(predicate: (index: number, it: T) => boolean): Sequence<T> distinct(): Sequence<T>
partition(predicate: (it: T) => boolean): [T[], T[]] distinctBy<K>(transform: (it: T) => K): Sequence<T>
drop(count: number): Sequence<T> drop(count: number): Sequence<T>
dropWhile(predicate: (it: T) => boolean): Sequence<T> dropWhile(predicate: (it: T) => boolean): Sequence<T>
dropUntil(predicate: (it: T) => boolean): Sequence<T> dropUntil(predicate: (it: T) => boolean): Sequence<T>
@@ -154,284 +159,279 @@ export type Sequence<T> = {
takeWhile(predicate: (it: T) => boolean): Sequence<T> takeWhile(predicate: (it: T) => boolean): Sequence<T>
takeUntil(predicate: (it: T) => boolean): Sequence<T> takeUntil(predicate: (it: T) => boolean): Sequence<T>
slice(start: number, end?: number): Sequence<T> slice(start: number, end?: number): Sequence<T>
distinct(): Sequence<T>
distinctBy<K>(transform: (it: T) => K): Sequence<T>
map<R>(transform: (it: T) => R): Sequence<R> map<R>(transform: (it: T) => R): Sequence<R>
mapIndexed<R>(transform: (index: number, it: T) => R): Sequence<R> mapNotNull<R>(transform: (it: T) => R | null): Sequence<R>
flatten<R>(this: Sequence<Iterable<R>>): Sequence<R> flatten<R>(this: Sequence<Iterable<R>>): Sequence<R>
flatMap<R>(transform: (it: T) => Iterable<R>): Sequence<R> flatMap<R>(transform: (it: T) => Iterable<R>): Sequence<R>
flatMapIndexed<R>(transform: (index: number, it: T) => Iterable<R>): Sequence<R>
groupBy<K>(transform: (it: T) => K): Sequence<[K, T[]]>
associate<K, V>(transform: (it: T) => [K, V]): Sequence<[K, V]> associate<K, V>(transform: (it: T) => [K, V]): Sequence<[K, V]>
associateBy<K>(transform: (it: T) => K): Sequence<[K, T]> associateBy<K>(transform: (it: T) => K): Sequence<[K, T]>
associateWith<V>(transform: (it: T) => V): Sequence<[T, V]> associateWith<V>(transform: (it: T) => V): Sequence<[T, V]>
zip<B>(items: Iterable<B>): Sequence<[T, B]> zip<B>(items: Iterable<B>): Sequence<[T, B]>
unzip<A, B>(this: Sequence<[A, B]>): [A[], B[]] unzip<A, B>(this: Sequence<[A, B]>): [Sequence<A>, Sequence<B>]
windowed(size: number, step?: number, partial?: boolean): Sequence<T[]> windowed(size: number, step?: number, partial?: boolean): Sequence<T[]>
chunked(size: number): Sequence<T[]> chunked(size: number): Sequence<T[]>
groupBy<K>(transform: (it: T) => K): [K, T[]][] reversed(): Sequence<T>
sorted(this: Sequence<number>): Sequence<number>
reversed(): T[] sortedWith(compare: (a: T, b: T) => number): Sequence<T>
sorted(this: Sequence<number>): number[] sortedBy(transform: (it: T) => number): Sequence<T>
sortedWith(compare: (a: T, b: T) => number): T[]
sortedBy(transform: (it: T) => number): T[]
joinToString(separator: string, transform?: (it: T) => string): string
} }
export function sequence<T>(value: Iterable<T>): Sequence<T> { export function sequence<T>(value: Iterable<T>): Sequence<T> {
return wrap(() => value); return wrap(() => value);
} }
export function wrap<T>(factory: () => Iterable<T>): Sequence<T> { function wrap<T>(value: Iterable<T> | (() => Iterable<T>)): Sequence<T> {
const resolve = () => typeof value === "function" ? value() : value;
return { return {
[Symbol.iterator]() { [Symbol.iterator]() {
return factory()[Symbol.iterator](); return resolve()[Symbol.iterator]();
}, },
collect() { toArray() {
return collect(factory()); return toArray(resolve());
},
toSet() {
return toSet(resolve());
},
toMap<K, V>() {
return toMap(resolve() as Iterable<[K, V]>);
}, },
onEach(action) { onEach(action) {
return wrap(() => onEach(factory(), action)); return wrap(() => onEach(resolve(), action));
}, },
forEach(action) { forEach(action) {
forEach(factory(), action); forEach(resolve(), action);
}, },
isEmpty() { isEmpty() {
return isEmpty(factory()); return isEmpty(resolve());
}, },
isNotEmpty() { isNotEmpty() {
return isNotEmpty(factory()); return isNotEmpty(resolve());
}, },
count(predicate) { count(predicate) {
return count(factory(), predicate); return count(resolve(), 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);
}, },
indices() { indices() {
return wrap(() => indices(factory())); return wrap(() => indices(resolve()));
}, },
withIndex() { withIndex() {
return wrap(() => withIndex(factory())); return wrap(() => withIndex(resolve()));
}, },
contains(item) { contains(item) {
return contains(factory(), item); return contains(resolve(), item);
}, },
containsAll(items) { containsAll(items) {
return containsAll(factory(), items); return containsAll(resolve(), items);
}, },
any(predicate) { any(predicate) {
return any(factory(), predicate); return any(resolve(), predicate);
}, },
all(predicate) { all(predicate) {
return all(factory(), predicate); return all(resolve(), predicate);
}, },
none(predicate) { none(predicate) {
return none(factory(), predicate); return none(resolve(), predicate);
}, },
get(index) { get(index) {
return get(factory(), index); return get(resolve(), index);
}, },
getOrNull(index) { getOrNull(index) {
return getOrNull(factory(), index); return getOrNull(resolve(), index);
}, },
first(predicate) { first(predicate) {
return first(factory(), predicate); return first(resolve(), predicate);
}, },
firstOrNull(predicate) { firstOrNull(predicate) {
return firstOrNull(factory(), predicate); return firstOrNull(resolve(), predicate);
}, },
last(predicate) { last(predicate) {
return last(factory(), predicate); return last(resolve(), predicate);
}, },
lastOrNull(predicate) { lastOrNull(predicate) {
return lastOrNull(factory(), predicate); return lastOrNull(resolve(), predicate);
}, },
single(predicate) { single(predicate) {
return single(factory(), predicate); return single(resolve(), predicate);
}, },
singleOrNull(predicate) { singleOrNull(predicate) {
return singleOrNull(factory(), predicate); return singleOrNull(resolve(), 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);
}, },
reduce(accumulate) { reduce(accumulate) {
return reduce(factory(), accumulate); return reduce(resolve(), accumulate);
}, },
reduceOrNull(accumulate) { reduceOrNull(accumulate) {
return reduceOrNull(factory(), accumulate); return reduceOrNull(resolve(), accumulate);
}, },
fold(initial, accumulate) { fold(initial, accumulate) {
return fold(factory(), initial, accumulate); return fold(resolve(), initial, accumulate);
}, },
scan(initial, accumulate) { scan(initial, accumulate) {
return wrap(() => scan(factory(), initial, accumulate)); return wrap(() => scan(resolve(), initial, accumulate));
}, },
sum() { sum() {
return sum(factory() as any); return sum(resolve() as Iterable<number>);
}, },
sumBy(transform) { sumBy(transform) {
return sumBy(factory(), transform); return sumBy(resolve(), transform);
}, },
average() { average() {
return average(factory() as any); return average(resolve() as Iterable<number>);
},
averageOrNull() {
return averageOrNull(resolve() as Iterable<number>);
}, },
averageBy(transform) { averageBy(transform) {
return averageBy(factory(), transform); return averageBy(resolve(), transform);
}, },
plus(items) { averageByOrNull(transform) {
return wrap(() => plus(factory(), items)); return averageByOrNull(resolve(), transform);
}, },
minus(items) { min() {
return wrap(() => minus(factory(), items)); return min(resolve() as Iterable<number>);
}, },
filter(predicate: (it: T) => boolean) { minOrNull() {
return wrap(() => filter(factory(), predicate)); return minOrNull(resolve() as Iterable<number>);
}, },
filterIndexed(predicate: (index: number, it: T) => boolean) { minWith(compare) {
return wrap(() => filterIndexed(factory(), predicate)); return minWith(resolve(), compare);
}, },
partition(predicate) { minWithOrNull(compare) {
return partition(factory(), predicate); return minWithOrNull(resolve(), compare);
}, },
drop(count) { minBy(transform) {
return wrap(() => drop(factory(), count)); return minBy(resolve(), transform);
}, },
dropWhile(predicate) { minByOrNull(transform) {
return wrap(() => dropWhile(factory(), predicate)); return minByOrNull(resolve(), transform);
}, },
dropUntil(predicate) { minOf(transform) {
return wrap(() => dropUntil(factory(), predicate)); return minOf(resolve(), transform);
}, },
take(count) { minOfOrNull(transform) {
return wrap(() => take(factory(), count)); return minOfOrNull(resolve(), transform);
}, },
takeWhile(predicate) { max() {
return wrap(() => takeWhile(factory(), predicate)); return max(resolve() as Iterable<number>);
}, },
takeUntil(predicate) { maxOrNull() {
return wrap(() => takeUntil(factory(), predicate)); return maxOrNull(resolve() as Iterable<number>);
}, },
slice(start, end) { maxWith(compare) {
return wrap(() => slice(factory(), start, end)); return maxWith(resolve(), compare);
}, },
distinct() { maxWithOrNull(compare) {
return wrap(() => distinct(factory())); return maxWithOrNull(resolve(), compare);
}, },
distinctBy(transform) { maxBy(transform) {
return wrap(() => distinctBy(factory(), transform)); return maxBy(resolve(), transform);
}, },
map(transform) { maxByOrNull(transform) {
return wrap(() => map(factory(), transform)); return maxByOrNull(resolve(), transform);
}, },
mapIndexed(transform) { maxOf(transform) {
return wrap(() => mapIndexed(factory(), transform)); return maxOf(resolve(), transform);
}, },
flatten() { maxOfOrNull(transform) {
return wrap(() => flatten(factory() as any)); return maxOfOrNull(resolve(), transform);
},
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);
}, },
joinToString(separator, 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<R>() {
return wrap(() => flatten(resolve() as Iterable<Iterable<R>>));
},
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<A, B>() {
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<number>));
},
sortedWith(compare) {
return wrap(sortedWith(resolve(), compare));
},
sortedBy(transform) {
return wrap(sortedBy(resolve(), transform));
}, },
}; };
} }

View File

@@ -1,3 +1,5 @@
import {error} from "../util/error";
export function average(value: Iterable<number>): number { export function average(value: Iterable<number>): number {
let count = 0; let count = 0;
let sum = 0; let sum = 0;
@@ -5,5 +7,5 @@ export function average(value: Iterable<number>): number {
count++; count++;
sum += it; sum += it;
} }
return count !== 0 ? sum / count : 0; return count !== 0 ? sum / count : error();
} }

View File

@@ -1,3 +1,5 @@
import {error} from "../util/error";
export function averageBy<T>(value: Iterable<T>, transform: (it: T) => number): number { export function averageBy<T>(value: Iterable<T>, transform: (it: T) => number): number {
let count = 0; let count = 0;
let sum = 0; let sum = 0;
@@ -5,5 +7,5 @@ export function averageBy<T>(value: Iterable<T>, transform: (it: T) => number):
count++; count++;
sum += transform(it); sum += transform(it);
} }
return count !== 0 ? sum / count : 0; return count !== 0 ? sum / count : error();
} }

View File

@@ -0,0 +1,9 @@
export function averageByOrNull<T>(value: Iterable<T>, 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;
}

View File

@@ -0,0 +1,9 @@
export function averageOrNull(value: Iterable<number>): number | null {
let count = 0;
let sum = 0;
for (const it of value) {
count++;
sum += it;
}
return count !== 0 ? sum / count : null;
}

View File

@@ -1,4 +1,7 @@
import {error} from "../util/error";
export function* chunked<T>(value: Iterable<T>, size: number): Iterable<T[]> { export function* chunked<T>(value: Iterable<T>, size: number): Iterable<T[]> {
if (size < 1) error();
let buffer: T[] = []; let buffer: T[] = [];
for (const it of value) { for (const it of value) {
buffer.push(it); buffer.push(it);

View File

@@ -1,3 +0,0 @@
export function collect<T>(value: Iterable<T>): T[] {
return [...value];
}

View File

@@ -1,7 +1,7 @@
export function containsAll<T>(value: Iterable<T>, items: Iterable<T>): boolean { export function containsAll<T>(value: Iterable<T>, items: Iterable<T>): boolean {
const set = new Set(items); const set = new Set(value);
for (const it of value) { for (const it of items) {
set.delete(it); if (!set.has(it)) return false;
} }
return set.size === 0; return true;
} }

View File

@@ -1,7 +1,13 @@
import {error} from "../util/error";
export function* drop<T>(value: Iterable<T>, count: number): Iterable<T> { export function* drop<T>(value: Iterable<T>, count: number): Iterable<T> {
if (count < 0) error();
let dropped = 0; let dropped = 0;
for (const it of value) { for (const it of value) {
if (dropped === count) yield it; if (dropped < count) {
else dropped++; dropped++;
continue;
}
yield it;
} }
} }

View File

@@ -1,12 +1,11 @@
export function* dropUntil<T>(value: Iterable<T>, predicate: (it: T) => boolean): Iterable<T> { export function* dropUntil<T>(value: Iterable<T>, predicate: (it: T) => boolean): Iterable<T> {
const iterator = value[Symbol.iterator](); const iterator = value[Symbol.iterator]();
let {value: it, done} = iterator.next(); let next = iterator.next();
while (!done) { while (!next.done && !predicate(next.value)) {
if (predicate(it)) break; next = iterator.next();
({value: it, done} = iterator.next());
} }
while (!done) { while (!next.done) {
yield it; yield next.value;
({value: it, done} = iterator.next()); next = iterator.next();
} }
} }

View File

@@ -1,12 +1,11 @@
export function* dropWhile<T>(value: Iterable<T>, predicate: (it: T) => boolean): Iterable<T> { export function* dropWhile<T>(value: Iterable<T>, predicate: (it: T) => boolean): Iterable<T> {
const iterator = value[Symbol.iterator](); const iterator = value[Symbol.iterator]();
let {value: it, done} = iterator.next(); let next = iterator.next();
while (!done) { while (!next.done && predicate(next.value)) {
if (!predicate(it)) break; next = iterator.next();
({value: it, done} = iterator.next());
} }
while (!done) { while (!next.done) {
yield it; yield next.value;
({value: it, done} = iterator.next()); next = iterator.next();
} }
} }

View File

@@ -1,6 +0,0 @@
export function* filterIndexed<T>(value: Iterable<T>, predicate: (index: number, it: T) => boolean): Iterable<T> {
let index = 0;
for (const it of value) {
if (predicate(index++, it)) yield it;
}
}

View File

@@ -1,4 +1,4 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function first<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T { export function first<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T {
for (const it of value) { for (const it of value) {

View File

@@ -1,10 +0,0 @@
import {error} from "../util/error.js";
export function firstIndex<T>(value: Iterable<T>, predicate?: (it: T) => boolean): number {
let index = 0;
for (const it of value) {
if (!predicate || predicate(it)) return index;
index++;
}
error();
}

View File

@@ -1,8 +0,0 @@
export function firstIndexOrNull<T>(value: Iterable<T>, predicate?: (it: T) => boolean): number | null {
let index = 0;
for (const it of value) {
if (!predicate || predicate(it)) return index;
index++;
}
return null;
}

View File

@@ -1,6 +0,0 @@
export function* flatMapIndexed<T, R>(value: Iterable<T>, transform: (index: number, it: T) => Iterable<R>): Iterable<R> {
let index = 0;
for (const it of value) {
yield* transform(index++, it);
}
}

View File

@@ -1,6 +1,7 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function get<T>(value: Iterable<T>, index: number): T { export function get<T>(value: Iterable<T>, index: number): T {
if (index < 0) error();
let i = 0; let i = 0;
for (const it of value) { for (const it of value) {
if (i++ === index) return it; if (i++ === index) return it;

View File

@@ -1,4 +1,5 @@
export function getOrNull<T>(value: Iterable<T>, index: number): T | null { export function getOrNull<T>(value: Iterable<T>, index: number): T | null {
if (index < 0) return null;
let i = 0; let i = 0;
for (const it of value) { for (const it of value) {
if (i++ === index) return it; if (i++ === index) return it;

View File

@@ -1,4 +1,4 @@
export function* indices(value: Iterable<unknown>): Iterable<number> { export function* indices<T>(value: Iterable<T>): Iterable<number> {
let index = 0; let index = 0;
for (const _ of value) { for (const _ of value) {
yield index++; yield index++;

View File

@@ -0,0 +1,6 @@
export function* intersect<T>(value: Iterable<T>, items: Iterable<T>): Iterable<T> {
const set = new Set(items);
for (const it of value) {
if (set.has(it)) yield it;
}
}

View File

@@ -1,13 +1,10 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function last<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T { export function last<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T {
let last!: T; let last: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (predicate && !predicate(it)) continue; if (!predicate || predicate(it)) last = it;
last = it;
found = true;
} }
if (!found) error(); if (last === undefined) error();
return last; return last;
} }

View File

@@ -1,12 +0,0 @@
import {error} from "../util/error.js";
export function lastIndex<T>(value: Iterable<T>, 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;
}

View File

@@ -1,10 +0,0 @@
export function lastIndexOrNull<T>(value: Iterable<T>, 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;
}

View File

@@ -1,11 +1,8 @@
export function lastOrNull<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T | null { export function lastOrNull<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T | null {
let last!: T; let last: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (predicate && !predicate(it)) continue; if (!predicate || predicate(it)) last = it;
last = it;
found = true;
} }
if (!found) return null; if (last === undefined) return null;
return last; return last;
} }

View File

@@ -1,6 +0,0 @@
export function* mapIndexed<T, R>(value: Iterable<T>, transform: (index: number, it: T) => R): Iterable<R> {
let index = 0;
for (const it of value) {
yield transform(index++, it);
}
}

View File

@@ -0,0 +1,6 @@
export function* mapNotNull<T, R>(value: Iterable<T>, transform: (it: T) => R | null): Iterable<R> {
for (const it of value) {
const transformed = transform(it);
if (transformed !== null) yield transformed;
}
}

View File

@@ -1,13 +1,10 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function max(value: Iterable<number>): number { export function max(value: Iterable<number>): number {
let max!: number; let max: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && it <= max) continue; if (max === undefined || it > max) max = it;
max = it;
found = true;
} }
if (!found) error(); if (max === undefined) error();
return max; return max;
} }

View File

@@ -1,16 +1,15 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function maxBy<T>(value: Iterable<T>, transform: (it: T) => number): T { export function maxBy<T>(value: Iterable<T>, transform: (it: T) => number): T {
let maxIt!: T; let max!: T;
let max!: number; let maxVal: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val <= max) continue; if (maxVal === undefined || val > maxVal) {
maxIt = it; max = it;
max = val; maxVal = val;
found = true;
} }
if (!found) error(); }
return maxIt; if (maxVal === undefined) error();
return max;
} }

View File

@@ -1,14 +1,13 @@
export function maxByOrNull<T>(value: Iterable<T>, transform: (it: T) => number): T | null { export function maxByOrNull<T>(value: Iterable<T>, transform: (it: T) => number): T | null {
let maxIt!: T; let max!: T;
let max!: number; let maxVal: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val <= max) continue; if (maxVal === undefined || val > maxVal) {
maxIt = it; max = it;
max = val; maxVal = val;
found = true;
} }
if (!found) return null; }
return maxIt; if (maxVal === undefined) return null;
return max;
} }

View File

@@ -1,14 +1,11 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function maxOf<T>(value: Iterable<T>, transform: (it: T) => number): number { export function maxOf<T>(value: Iterable<T>, transform: (it: T) => number): number {
let max!: number; let max: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val <= max) continue; if (max === undefined || val > max) max = val;
max = val;
found = true;
} }
if (!found) error(); if (max === undefined) error();
return max; return max;
} }

View File

@@ -1,12 +1,9 @@
export function maxOfOrNull<T>(value: Iterable<T>, transform: (it: T) => number): number | null { export function maxOfOrNull<T>(value: Iterable<T>, transform: (it: T) => number): number | null {
let max!: number; let max: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val <= max) continue; if (max === undefined || val > max) max = val;
max = val;
found = true;
} }
if (!found) return null; if (max === undefined) return null;
return max; return max;
} }

View File

@@ -1,11 +1,8 @@
export function maxOrNull(value: Iterable<number>): number | null { export function maxOrNull(value: Iterable<number>): number | null {
let max!: number; let max: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && it <= max) continue; if (max === undefined || it > max) max = it;
max = it;
found = true;
} }
if (!found) return null; if (max === undefined) return null;
return max; return max;
} }

View File

@@ -1,13 +1,10 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function maxWith<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T { export function maxWith<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T {
let max!: T; let max: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && compare(it, max) <= 0) continue; if (max === undefined || compare(it, max) > 0) max = it;
max = it;
found = true;
} }
if (!found) error(); if (max === undefined) error();
return max; return max;
} }

View File

@@ -1,11 +1,8 @@
export function maxWithOrNull<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T | null { export function maxWithOrNull<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T | null {
let max!: T; let max: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && compare(it, max) <= 0) continue; if (max === undefined || compare(it, max) > 0) max = it;
max = it;
found = true;
} }
if (!found) return null; if (max === undefined) return null;
return max; return max;
} }

View File

@@ -1,13 +1,10 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function min(value: Iterable<number>): number { export function min(value: Iterable<number>): number {
let min!: number; let min: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && it >= min) continue; if (min === undefined || it < min) min = it;
min = it;
found = true;
} }
if (!found) error(); if (min === undefined) error();
return min; return min;
} }

View File

@@ -1,16 +1,15 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function minBy<T>(value: Iterable<T>, transform: (it: T) => number): T { export function minBy<T>(value: Iterable<T>, transform: (it: T) => number): T {
let minIt!: T; let minIt!: T;
let min!: number; let minVal: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val >= min) continue; if (minVal === undefined || val < minVal) {
minIt = it; minIt = it;
min = val; minVal = val;
found = true;
} }
if (!found) error(); }
if (minVal === undefined) error();
return minIt; return minIt;
} }

View File

@@ -1,14 +1,13 @@
export function minByOrNull<T>(value: Iterable<T>, transform: (it: T) => number): T | null { export function minByOrNull<T>(value: Iterable<T>, transform: (it: T) => number): T | null {
let minIt!: T; let minIt!: T;
let min!: number; let minVal: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val >= min) continue; if (minVal === undefined || val < minVal) {
minIt = it; minIt = it;
min = val; minVal = val;
found = true;
} }
if (!found) return null; }
if (minVal === undefined) return null;
return minIt; return minIt;
} }

View File

@@ -1,14 +1,11 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function minOf<T>(value: Iterable<T>, transform: (it: T) => number): number { export function minOf<T>(value: Iterable<T>, transform: (it: T) => number): number {
let min!: number; let min: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val >= min) continue; if (min === undefined || val < min) min = val;
min = val;
found = true;
} }
if (!found) error(); if (min === undefined) error();
return min; return min;
} }

View File

@@ -1,12 +1,9 @@
export function minOfOrNull<T>(value: Iterable<T>, transform: (it: T) => number): number | null { export function minOfOrNull<T>(value: Iterable<T>, transform: (it: T) => number): number | null {
let min!: number; let min: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
const val = transform(it); const val = transform(it);
if (found && val >= min) continue; if (min === undefined || val < min) min = val;
min = val;
found = true;
} }
if (!found) return null; if (min === undefined) return null;
return min; return min;
} }

View File

@@ -1,11 +1,8 @@
export function minOrNull(value: Iterable<number>): number | null { export function minOrNull(value: Iterable<number>): number | null {
let min!: number; let min: number | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (found && it <= min) continue; if (min === undefined || it < min) min = it;
min = it;
found = true;
} }
if (!found) return null; if (min === undefined) return null;
return min; return min;
} }

View File

@@ -1,14 +1,10 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function minWith<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T { export function minWith<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T {
const iterator = value[Symbol.iterator](); let min: T | undefined;
let {value: it, done} = iterator.next(); for (const it of value) {
if (done) error(); if (min === undefined || compare(it, min) < 0) min = it;
let min = it;
({value: it, done} = iterator.next());
while (!done) {
if (compare(it, min) < 0) min = it;
({value: it, done} = iterator.next());
} }
if (min === undefined) error();
return min; return min;
} }

View File

@@ -1,12 +1,8 @@
export function minWithOrNull<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T | null { export function minWithOrNull<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T | null {
const iterator = value[Symbol.iterator](); let min: T | undefined;
let {value: it, done} = iterator.next(); for (const it of value) {
if (done) return null; if (min === undefined || compare(it, min) < 0) min = it;
let min = it;
({value: it, done} = iterator.next());
while (!done) {
if (compare(it, min) < 0) min = it;
({value: it, done} = iterator.next());
} }
if (min === undefined) return null;
return min; return min;
} }

View File

@@ -1,9 +1,30 @@
export function partition<T>(value: Iterable<T>, predicate: (it: T) => boolean): [T[], T[]] { export function partition<T>(value: Iterable<T>, predicate: (it: T) => boolean): [() => Iterable<T>, () => Iterable<T>] {
const iterator = value[Symbol.iterator]();
let finished = false;
const t: T[] = []; const t: T[] = [];
const f: T[] = []; const f: T[] = [];
for (const it of value) {
if (predicate(it)) t.push(it); function next() {
else f.push(it); const {value, done} = iterator.next();
if (done) finished = true;
else (predicate(value) ? t : f).push(value);
} }
return [t, f];
function* T(): Iterable<T> {
let i = 0;
while (!finished || i < t.length) {
if (i < t.length) yield t[i++];
else next();
}
}
function* F(): Iterable<T> {
let i = 0;
while (!finished || i < f.length) {
if (i < f.length) yield f[i++];
else next();
}
}
return [T, F];
} }

View File

@@ -1,8 +1,8 @@
export function* plus<T>(value1: Iterable<T>, value2: Iterable<T>): Iterable<T> { export function* plus<T>(value: Iterable<T>, items: Iterable<T>): Iterable<T> {
for (const it of value1) { for (const it of value) {
yield it; yield it;
} }
for (const it of value2) { for (const it of items) {
yield it; yield it;
} }
} }

View File

@@ -1,14 +1,14 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function reduce<T>(value: Iterable<T>, accumulate: (acc: T, it: T) => T): T { export function reduce<T>(value: Iterable<T>, accumulate: (acc: T, it: T) => T): T {
const iterator = value[Symbol.iterator](); const iterator = value[Symbol.iterator]();
let {value: it, done} = iterator.next(); const first = iterator.next();
if (done) error(); if (first.done) error();
let acc = it let acc = first.value;
({value: it, done} = iterator.next()); let next = iterator.next();
while (!done) { while (!next.done) {
acc = accumulate(acc, it); acc = accumulate(acc, next.value);
({value: it, done} = iterator.next()); next = iterator.next();
} }
return acc; return acc;
} }

View File

@@ -1,12 +1,12 @@
export function reduceOrNull<T>(value: Iterable<T>, accumulate: (acc: T, it: T) => T): T | null { export function reduceOrNull<T>(value: Iterable<T>, accumulate: (acc: T, it: T) => T): T | null {
const iterator = value[Symbol.iterator](); const iterator = value[Symbol.iterator]();
let {value: it, done} = iterator.next(); const first = iterator.next();
if (done) return null; if (first.done) return null;
let acc = it let acc = first.value;
({value: it, done} = iterator.next()); let next = iterator.next();
while (!done) { while (!next.done) {
acc = accumulate(acc, it); acc = accumulate(acc, next.value);
({value: it, done} = iterator.next()); next = iterator.next();
} }
return acc; return acc;
} }

View File

@@ -1,3 +1,3 @@
export function reversed<T>(iterable: Iterable<T>): T[] { export function reversed<T>(value: Iterable<T>): T[] {
return [...iterable].reverse(); return [...value].reverse();
} }

View File

@@ -1,14 +1,13 @@
import {error} from "../util/error.js"; import {error} from "../util/error";
export function single<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T { export function single<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T {
let last!: T; let last: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (predicate && !predicate(it)) continue; if (!predicate || predicate(it)) {
if (found) error(); if (last !== undefined) error();
last = it; last = it;
found = true;
} }
if (!found) error(); }
if (last === undefined) error();
return last; return last;
} }

View File

@@ -1,12 +1,11 @@
export function singleOrNull<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T | null { export function singleOrNull<T>(value: Iterable<T>, predicate?: (it: T) => boolean): T | null {
let last!: T; let last: T | undefined;
let found = false;
for (const it of value) { for (const it of value) {
if (predicate && !predicate(it)) continue; if (!predicate || predicate(it)) {
if (found) return null; if (last !== undefined) return null;
last = it; last = it;
found = true;
} }
if (!found) return null; }
if (last === undefined) return null;
return last; return last;
} }

View File

@@ -1,7 +1,16 @@
export function* slice<T>(value: Iterable<T>, start: number, endInclusive?: number): Iterable<T> { import {error} from "../util/error";
export function* slice<T>(value: Iterable<T>, start: number, end?: number): Iterable<T> {
if (start < 0) error();
if (end !== undefined) {
if (end < 0) error();
if (start > end) error();
}
let index = 0; let index = 0;
for (const it of value) { 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();
} }

View File

@@ -1,3 +1,3 @@
export function sorted(iterable: Iterable<number>): number[] { export function sorted(value: Iterable<number>): number[] {
return [...iterable].sort((a, b) => a - b); return [...value].sort((a, b) => a - b);
} }

View File

@@ -1,3 +1,3 @@
export function sortedBy<T>(iterable: Iterable<T>, transform: (it: T) => number): T[] { export function sortedBy<T>(value: Iterable<T>, transform: (it: T) => number): T[] {
return [...iterable].sort((a, b) => transform(a) - transform(b)); return [...value].sort((a, b) => transform(a) - transform(b));
} }

View File

@@ -1,3 +1,3 @@
export function sortedWith<T>(iterable: Iterable<T>, compare: (a: T, b: T) => number): T[] { export function sortedWith<T>(value: Iterable<T>, compare: (a: T, b: T) => number): T[] {
return [...iterable].sort(compare); return [...value].sort(compare);
} }

View File

@@ -1,7 +1,11 @@
import {error} from "../util/error";
export function* take<T>(value: Iterable<T>, count: number): Iterable<T> { export function* take<T>(value: Iterable<T>, count: number): Iterable<T> {
if (count < 0) error();
let taken = 0; let taken = 0;
for (const it of value) { for (const it of value) {
if (taken++ === count) return;
yield it; yield it;
taken++;
if (taken === count) return;
} }
} }

3
src/sequence/toArray.ts Normal file
View File

@@ -0,0 +1,3 @@
export function toArray<T>(value: Iterable<T>): T[] {
return [...value];
}

3
src/sequence/toMap.ts Normal file
View File

@@ -0,0 +1,3 @@
export function toMap<K, V>(value: Iterable<[K, V]>): Map<K, V> {
return new Map(value);
}

3
src/sequence/toSet.ts Normal file
View File

@@ -0,0 +1,3 @@
export function toSet<T>(value: Iterable<T>): Set<T> {
return new Set(value);
}

View File

@@ -1,12 +1,29 @@
export function unzip<A, B>(value: Iterable<[A, B]>): [A[], B[]] { export function unzip<A, B>(value: Iterable<[A, B]>): [() => Iterable<A>, () => Iterable<B>] {
const a: A[] = [];
const b: B[] = [];
const iterator = value[Symbol.iterator](); const iterator = value[Symbol.iterator]();
let {value: it, done} = iterator.next(); let finished = false;
while (!done) { const cache: [A, B][] = [];
a.push(it[0]);
b.push(it[1]); function next() {
({value: it, done} = iterator.next()); const {value, done} = iterator.next();
if (done) finished = true;
else cache.push(value);
} }
return [a, b];
function* A(): Iterable<A> {
let i = 0;
while (!finished || i < cache.length) {
if (i < cache.length) yield cache[i++][0];
else next();
}
}
function* B(): Iterable<B> {
let i = 0;
while (!finished || i < cache.length) {
if (i < cache.length) yield cache[i++][1];
else next();
}
}
return [A, B];
} }

View File

@@ -1,4 +1,8 @@
import {error} from "../util/error";
export function* windowed<T>(value: Iterable<T>, size: number, step: number = 1, partial: boolean = false): Iterable<T[]> { export function* windowed<T>(value: Iterable<T>, size: number, step: number = 1, partial: boolean = false): Iterable<T[]> {
if (size < 1) error();
if (step < 1) error();
const buffer: T[] = []; const buffer: T[] = [];
for (const it of value) { for (const it of value) {
buffer.push(it); buffer.push(it);
@@ -7,9 +11,10 @@ export function* windowed<T>(value: Iterable<T>, size: number, step: number = 1,
buffer.splice(0, step); buffer.splice(0, step);
} }
} }
if (!partial) return; if (partial) {
while (buffer.length !== 0) { while (buffer.length !== 0) {
yield [...buffer]; yield [...buffer];
buffer.splice(0, step); buffer.splice(0, step);
} }
}
} }

View File

@@ -1,9 +1,11 @@
export function* zip<T, B>(value: Iterable<T>, items: Iterable<B>): Iterable<[T, B]> { export function* zip<T, B>(value: Iterable<T>, items: Iterable<B>): Iterable<[T, B]> {
const iteratorA = value[Symbol.iterator](); const iteratorA = value[Symbol.iterator]();
const iteratorB = items[Symbol.iterator](); const iteratorB = items[Symbol.iterator]();
let {value: itA, done: doneA} = iteratorA.next(); let a = iteratorA.next();
let {value: itB, done: doneB} = iteratorB.next(); let b = iteratorB.next();
while (!doneA && !doneB) { while (!a.done && !b.done) {
yield [itA, itB]; yield [a.value, b.value];
a = iteratorA.next();
b = iteratorB.next();
} }
} }

View File

@@ -1,11 +1,13 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ESNext", "target": "ESNext",
"rootDir": "./src/", "module": "ESNext",
"outDir": "./dist/", "moduleResolution": "Bundler",
"strict": true,
"declaration": true, "declaration": true,
"strict": true "outDir": "./dist/"
} },
"include": [
"./src/"
]
} }