diff --git a/src/index.ts b/src/index.ts index e69de29..4e20076 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1 @@ +export {sequence} from "./sequence.js"; diff --git a/src/sequence.ts b/src/sequence.ts new file mode 100644 index 0000000..3a147bf --- /dev/null +++ b/src/sequence.ts @@ -0,0 +1,437 @@ +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"; + +export type Sequence = { + [Symbol.iterator](): Iterator + collect(): T[] + + onEach(action: (it: T) => void): void + 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]> + + contains(item: T): boolean + containsAll(items: Iterable): boolean + any(predicate: (it: T) => boolean): boolean + all(predicate: (it: T) => boolean): boolean + none(predicate: (it: T) => boolean): boolean + + get(index: number): T + getOrNull(index: number): T | null + first(predicate?: (it: T) => boolean): T + firstOrNull(predicate?: (it: T) => boolean): T | null + last(predicate?: (it: T) => boolean): T + lastOrNull(predicate?: (it: T) => boolean): T | null + single(predicate?: (it: T) => boolean): T + singleOrNull(predicate?: (it: T) => boolean): T | null + + min(this: Sequence): number + minOrNull(this: Sequence): number | null + minWith(compare: (a: T, b: T) => number): T + minWithOrNull(compare: (a: T, b: T) => number): T | null + minBy(transform: (it: T) => number): T + 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 + maxWithOrNull(compare: (a: T, b: T) => number): T | null + maxBy(transform: (it: T) => number): T + maxByOrNull(transform: (it: T) => number): T | null + 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 + + plus(items: Iterable): Sequence + minus(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[]] + drop(count: number): Sequence + dropWhile(predicate: (it: T) => boolean): Sequence + dropUntil(predicate: (it: T) => boolean): Sequence + take(count: number): 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 + flatten(this: Sequence>): Sequence + flatMap(transform: (it: T) => Iterable): Sequence + flatMapIndexed(transform: (index: number, it: T) => Iterable): Sequence + 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[]] + + 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 +} + +export function sequence(value: Iterable): Sequence { + return wrap(() => value); +} + +export function wrap(factory: () => Iterable): Sequence { + return { + [Symbol.iterator]() { + return factory()[Symbol.iterator](); + }, + collect() { + return collect(factory()); + }, + onEach(action) { + return wrap(() => onEach(factory(), action)); + }, + forEach(action) { + forEach(factory(), action); + }, + isEmpty() { + return isEmpty(factory()); + }, + isNotEmpty() { + return isNotEmpty(factory()); + }, + 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); + }, + indices() { + return wrap(() => indices(factory())); + }, + withIndex() { + return wrap(() => withIndex(factory())); + }, + contains(item) { + return contains(factory(), item); + }, + containsAll(items) { + return containsAll(factory(), items); + }, + any(predicate) { + return any(factory(), predicate); + }, + all(predicate) { + return all(factory(), predicate); + }, + none(predicate) { + return none(factory(), predicate); + }, + get(index) { + return get(factory(), index); + }, + getOrNull(index) { + return getOrNull(factory(), index); + }, + first(predicate) { + return first(factory(), predicate); + }, + firstOrNull(predicate) { + return firstOrNull(factory(), predicate); + }, + last(predicate) { + return last(factory(), predicate); + }, + lastOrNull(predicate) { + return lastOrNull(factory(), predicate); + }, + single(predicate) { + return single(factory(), 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); + }, + reduce(accumulate) { + return reduce(factory(), accumulate); + }, + reduceOrNull(accumulate) { + return reduceOrNull(factory(), accumulate); + }, + fold(initial, accumulate) { + return fold(factory(), initial, accumulate); + }, + scan(initial, accumulate) { + return wrap(() => scan(factory(), initial, accumulate)); + }, + sum() { + return sum(factory() as any); + }, + sumBy(transform) { + return sumBy(factory(), transform); + }, + average() { + return average(factory() as any); + }, + averageBy(transform) { + return averageBy(factory(), transform); + }, + plus(items) { + return wrap(() => plus(factory(), items)); + }, + minus(items) { + return wrap(() => minus(factory(), items)); + }, + filter(predicate: (it: T) => boolean) { + return wrap(() => filter(factory(), predicate)); + }, + filterIndexed(predicate: (index: number, it: T) => boolean) { + return wrap(() => filterIndexed(factory(), predicate)); + }, + partition(predicate) { + return partition(factory(), predicate); + }, + drop(count) { + return wrap(() => drop(factory(), count)); + }, + dropWhile(predicate) { + return wrap(() => dropWhile(factory(), predicate)); + }, + dropUntil(predicate) { + return wrap(() => dropUntil(factory(), predicate)); + }, + take(count) { + return wrap(() => take(factory(), count)); + }, + takeWhile(predicate) { + return wrap(() => takeWhile(factory(), predicate)); + }, + takeUntil(predicate) { + return wrap(() => takeUntil(factory(), predicate)); + }, + slice(start, end) { + return wrap(() => slice(factory(), start, end)); + }, + distinct() { + return wrap(() => distinct(factory())); + }, + distinctBy(transform) { + return wrap(() => distinctBy(factory(), transform)); + }, + map(transform) { + return wrap(() => map(factory(), transform)); + }, + mapIndexed(transform) { + return wrap(() => mapIndexed(factory(), 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); + }, + joinToString(separator, transform) { + return joinToString(factory(), separator, transform); + }, + }; +} diff --git a/src/sequence/all.ts b/src/sequence/all.ts new file mode 100644 index 0000000..222b99e --- /dev/null +++ b/src/sequence/all.ts @@ -0,0 +1,6 @@ +export function all(value: Iterable, predicate: (it: T) => boolean): boolean { + for (const it of value) { + if (!predicate(it)) return false; + } + return true; +} diff --git a/src/sequence/any.ts b/src/sequence/any.ts new file mode 100644 index 0000000..f24eb72 --- /dev/null +++ b/src/sequence/any.ts @@ -0,0 +1,6 @@ +export function any(value: Iterable, predicate: (it: T) => boolean): boolean { + for (const it of value) { + if (predicate(it)) return true; + } + return false; +} diff --git a/src/sequence/associate.ts b/src/sequence/associate.ts new file mode 100644 index 0000000..2458ee4 --- /dev/null +++ b/src/sequence/associate.ts @@ -0,0 +1,5 @@ +export function* associate(value: Iterable, transform: (it: T) => [K, V]): Iterable<[K, V]> { + for (const it of value) { + yield transform(it); + } +} diff --git a/src/sequence/associateBy.ts b/src/sequence/associateBy.ts new file mode 100644 index 0000000..93cd1a7 --- /dev/null +++ b/src/sequence/associateBy.ts @@ -0,0 +1,5 @@ +export function* associateBy(value: Iterable, transform: (it: T) => K): Iterable<[K, T]> { + for (const it of value) { + yield [transform(it), it]; + } +} diff --git a/src/sequence/associateWith.ts b/src/sequence/associateWith.ts new file mode 100644 index 0000000..5579acc --- /dev/null +++ b/src/sequence/associateWith.ts @@ -0,0 +1,5 @@ +export function* associateWith(value: Iterable, transform: (it: T) => V): Iterable<[T, V]> { + for (const it of value) { + yield [it, transform(it)]; + } +} diff --git a/src/sequence/average.ts b/src/sequence/average.ts new file mode 100644 index 0000000..c35528f --- /dev/null +++ b/src/sequence/average.ts @@ -0,0 +1,9 @@ +export function average(value: Iterable): number { + let count = 0; + let sum = 0; + for (const it of value) { + count++; + sum += it; + } + return count !== 0 ? sum / count : 0; +} diff --git a/src/sequence/averageBy.ts b/src/sequence/averageBy.ts new file mode 100644 index 0000000..5325254 --- /dev/null +++ b/src/sequence/averageBy.ts @@ -0,0 +1,9 @@ +export function averageBy(value: Iterable, transform: (it: T) => number): number { + let count = 0; + let sum = 0; + for (const it of value) { + count++; + sum += transform(it); + } + return count !== 0 ? sum / count : 0; +} diff --git a/src/sequence/chunked.ts b/src/sequence/chunked.ts new file mode 100644 index 0000000..f22fd9f --- /dev/null +++ b/src/sequence/chunked.ts @@ -0,0 +1,11 @@ +export function* chunked(value: Iterable, size: number): Iterable { + let buffer: T[] = []; + for (const it of value) { + buffer.push(it); + if (buffer.length === size) { + yield buffer; + buffer = []; + } + } + if (buffer.length !== 0) yield buffer; +} diff --git a/src/sequence/collect.ts b/src/sequence/collect.ts new file mode 100644 index 0000000..bc52b36 --- /dev/null +++ b/src/sequence/collect.ts @@ -0,0 +1,3 @@ +export function collect(value: Iterable): T[] { + return [...value]; +} diff --git a/src/sequence/contains.ts b/src/sequence/contains.ts new file mode 100644 index 0000000..8c2a39b --- /dev/null +++ b/src/sequence/contains.ts @@ -0,0 +1,6 @@ +export function contains(value: Iterable, item: T): boolean { + for (const it of value) { + if (it === item) return true; + } + return false; +} diff --git a/src/sequence/containsAll.ts b/src/sequence/containsAll.ts new file mode 100644 index 0000000..9ae4595 --- /dev/null +++ b/src/sequence/containsAll.ts @@ -0,0 +1,7 @@ +export function containsAll(value: Iterable, items: Iterable): boolean { + const set = new Set(items); + for (const it of value) { + set.delete(it); + } + return set.size === 0; +} diff --git a/src/sequence/count.ts b/src/sequence/count.ts new file mode 100644 index 0000000..98ecd63 --- /dev/null +++ b/src/sequence/count.ts @@ -0,0 +1,7 @@ +export function count(value: Iterable, predicate?: (it: T) => boolean): number { + let count = 0; + for (const it of value) { + if (!predicate || predicate(it)) count++; + } + return count; +} diff --git a/src/sequence/distinct.ts b/src/sequence/distinct.ts new file mode 100644 index 0000000..d4b9087 --- /dev/null +++ b/src/sequence/distinct.ts @@ -0,0 +1,8 @@ +export function* distinct(value: Iterable): Iterable { + const set: Set = new Set(); + for (const it of value) { + if (set.has(it)) continue; + set.add(it); + yield it; + } +} diff --git a/src/sequence/distinctBy.ts b/src/sequence/distinctBy.ts new file mode 100644 index 0000000..7f078e4 --- /dev/null +++ b/src/sequence/distinctBy.ts @@ -0,0 +1,9 @@ +export function* distinctBy(value: Iterable, transform: (it: T) => K): Iterable { + const set: Set = new Set(); + for (const it of value) { + const x = transform(it); + if (set.has(x)) continue; + set.add(x); + yield it; + } +} diff --git a/src/sequence/drop.ts b/src/sequence/drop.ts new file mode 100644 index 0000000..c8a1567 --- /dev/null +++ b/src/sequence/drop.ts @@ -0,0 +1,7 @@ +export function* drop(value: Iterable, count: number): Iterable { + let dropped = 0; + for (const it of value) { + if (dropped === count) yield it; + else dropped++; + } +} diff --git a/src/sequence/dropUntil.ts b/src/sequence/dropUntil.ts new file mode 100644 index 0000000..305329c --- /dev/null +++ b/src/sequence/dropUntil.ts @@ -0,0 +1,12 @@ +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()); + } + while (!done) { + yield it; + ({value: it, done} = iterator.next()); + } +} diff --git a/src/sequence/dropWhile.ts b/src/sequence/dropWhile.ts new file mode 100644 index 0000000..94af9d2 --- /dev/null +++ b/src/sequence/dropWhile.ts @@ -0,0 +1,12 @@ +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()); + } + while (!done) { + yield it; + ({value: it, done} = iterator.next()); + } +} diff --git a/src/sequence/filter.ts b/src/sequence/filter.ts new file mode 100644 index 0000000..e151167 --- /dev/null +++ b/src/sequence/filter.ts @@ -0,0 +1,5 @@ +export function* filter(value: Iterable, predicate: (it: T) => boolean): Iterable { + for (const it of value) { + if (predicate(it)) yield it; + } +} diff --git a/src/sequence/filterIndexed.ts b/src/sequence/filterIndexed.ts new file mode 100644 index 0000000..a5850b0 --- /dev/null +++ b/src/sequence/filterIndexed.ts @@ -0,0 +1,6 @@ +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 new file mode 100644 index 0000000..dda1b8d --- /dev/null +++ b/src/sequence/first.ts @@ -0,0 +1,8 @@ +import {error} from "../util/error.js"; + +export function first(value: Iterable, predicate?: (it: T) => boolean): T { + for (const it of value) { + if (!predicate || predicate(it)) return it; + } + error(); +} diff --git a/src/sequence/firstIndex.ts b/src/sequence/firstIndex.ts new file mode 100644 index 0000000..32f1d9b --- /dev/null +++ b/src/sequence/firstIndex.ts @@ -0,0 +1,10 @@ +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 new file mode 100644 index 0000000..9e5b883 --- /dev/null +++ b/src/sequence/firstIndexOrNull.ts @@ -0,0 +1,8 @@ +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/firstOrNull.ts b/src/sequence/firstOrNull.ts new file mode 100644 index 0000000..d47476e --- /dev/null +++ b/src/sequence/firstOrNull.ts @@ -0,0 +1,6 @@ +export function firstOrNull(value: Iterable, predicate?: (it: T) => boolean): T | null { + for (const it of value) { + if (!predicate || predicate(it)) return it; + } + return null; +} diff --git a/src/sequence/flatMap.ts b/src/sequence/flatMap.ts new file mode 100644 index 0000000..bc5d12b --- /dev/null +++ b/src/sequence/flatMap.ts @@ -0,0 +1,5 @@ +export function* flatMap(value: Iterable, transform: (it: T) => Iterable): Iterable { + for (const it of value) { + yield* transform(it); + } +} diff --git a/src/sequence/flatMapIndexed.ts b/src/sequence/flatMapIndexed.ts new file mode 100644 index 0000000..cc573e7 --- /dev/null +++ b/src/sequence/flatMapIndexed.ts @@ -0,0 +1,6 @@ +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/flatten.ts b/src/sequence/flatten.ts new file mode 100644 index 0000000..83147f2 --- /dev/null +++ b/src/sequence/flatten.ts @@ -0,0 +1,5 @@ +export function* flatten(value: Iterable>): Iterable { + for (const it of value) { + yield* it; + } +} diff --git a/src/sequence/fold.ts b/src/sequence/fold.ts new file mode 100644 index 0000000..cf91f53 --- /dev/null +++ b/src/sequence/fold.ts @@ -0,0 +1,7 @@ +export function fold(value: Iterable, initial: R, accumulate: (acc: R, it: T) => R): R { + let acc = initial; + for (const it of value) { + acc = accumulate(acc, it); + } + return acc; +} diff --git a/src/sequence/forEach.ts b/src/sequence/forEach.ts new file mode 100644 index 0000000..70716f9 --- /dev/null +++ b/src/sequence/forEach.ts @@ -0,0 +1,5 @@ +export function forEach(value: Iterable, action: (it: T) => void) { + for (const it of value) { + action(it); + } +} diff --git a/src/sequence/get.ts b/src/sequence/get.ts new file mode 100644 index 0000000..682ac3e --- /dev/null +++ b/src/sequence/get.ts @@ -0,0 +1,9 @@ +import {error} from "../util/error.js"; + +export function get(value: Iterable, index: number): T { + let i = 0; + for (const it of value) { + if (i++ === index) return it; + } + error(); +} diff --git a/src/sequence/getOrNull.ts b/src/sequence/getOrNull.ts new file mode 100644 index 0000000..a2a017d --- /dev/null +++ b/src/sequence/getOrNull.ts @@ -0,0 +1,7 @@ +export function getOrNull(value: Iterable, index: number): T | null { + let i = 0; + for (const it of value) { + if (i++ === index) return it; + } + return null; +} diff --git a/src/sequence/groupBy.ts b/src/sequence/groupBy.ts new file mode 100644 index 0000000..b32e855 --- /dev/null +++ b/src/sequence/groupBy.ts @@ -0,0 +1,9 @@ +export function groupBy(value: Iterable, transform: (it: T) => K): [K, T[]][] { + const map: Map = new Map(); + for (const it of value) { + const key = transform(it); + if (!map.has(key)) map.set(key, []); + map.get(key)!.push(it); + } + return [...map]; +} diff --git a/src/sequence/indices.ts b/src/sequence/indices.ts new file mode 100644 index 0000000..da86ccb --- /dev/null +++ b/src/sequence/indices.ts @@ -0,0 +1,6 @@ +export function* indices(value: Iterable): Iterable { + let index = 0; + for (const _ of value) { + yield index++; + } +} diff --git a/src/sequence/isEmpty.ts b/src/sequence/isEmpty.ts new file mode 100644 index 0000000..8e77600 --- /dev/null +++ b/src/sequence/isEmpty.ts @@ -0,0 +1,4 @@ +export function isEmpty(value: Iterable): boolean { + const {done} = value[Symbol.iterator]().next(); + return done ?? false; +} diff --git a/src/sequence/isNotEmpty.ts b/src/sequence/isNotEmpty.ts new file mode 100644 index 0000000..a7a1b90 --- /dev/null +++ b/src/sequence/isNotEmpty.ts @@ -0,0 +1,4 @@ +export function isNotEmpty(value: Iterable): boolean { + const {done} = value[Symbol.iterator]().next(); + return !(done ?? false); +} diff --git a/src/sequence/joinToString.ts b/src/sequence/joinToString.ts new file mode 100644 index 0000000..64b381e --- /dev/null +++ b/src/sequence/joinToString.ts @@ -0,0 +1,7 @@ +export function joinToString(value: Iterable, separator: string, transform?: (it: T) => string): string { + const transformed: string[] = []; + for (const it of value) { + transformed.push(transform ? transform(it) : String(it)); + } + return transformed.join(separator); +} diff --git a/src/sequence/last.ts b/src/sequence/last.ts new file mode 100644 index 0000000..2077529 --- /dev/null +++ b/src/sequence/last.ts @@ -0,0 +1,13 @@ +import {error} from "../util/error.js"; + +export function last(value: Iterable, predicate?: (it: T) => boolean): T { + let last!: T; + let found = false; + for (const it of value) { + if (predicate && !predicate(it)) continue; + last = it; + found = true; + } + if (!found) error(); + return last; +} diff --git a/src/sequence/lastIndex.ts b/src/sequence/lastIndex.ts new file mode 100644 index 0000000..a6c1dba --- /dev/null +++ b/src/sequence/lastIndex.ts @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000..e9a44e6 --- /dev/null +++ b/src/sequence/lastIndexOrNull.ts @@ -0,0 +1,10 @@ +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 new file mode 100644 index 0000000..012bcb5 --- /dev/null +++ b/src/sequence/lastOrNull.ts @@ -0,0 +1,11 @@ +export function lastOrNull(value: Iterable, predicate?: (it: T) => boolean): T | null { + let last!: T; + let found = false; + for (const it of value) { + if (predicate && !predicate(it)) continue; + last = it; + found = true; + } + if (!found) return null; + return last; +} diff --git a/src/sequence/map.ts b/src/sequence/map.ts new file mode 100644 index 0000000..02b5348 --- /dev/null +++ b/src/sequence/map.ts @@ -0,0 +1,5 @@ +export function* map(value: Iterable, transform: (it: T) => R): Iterable { + for (const it of value) { + yield transform(it); + } +} diff --git a/src/sequence/mapIndexed.ts b/src/sequence/mapIndexed.ts new file mode 100644 index 0000000..d9870f6 --- /dev/null +++ b/src/sequence/mapIndexed.ts @@ -0,0 +1,6 @@ +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/max.ts b/src/sequence/max.ts new file mode 100644 index 0000000..103865b --- /dev/null +++ b/src/sequence/max.ts @@ -0,0 +1,13 @@ +import {error} from "../util/error.js"; + +export function max(value: Iterable): number { + let max!: number; + let found = false; + for (const it of value) { + if (found && it <= max) continue; + max = it; + found = true; + } + if (!found) error(); + return max; +} diff --git a/src/sequence/maxBy.ts b/src/sequence/maxBy.ts new file mode 100644 index 0000000..e1ffa9b --- /dev/null +++ b/src/sequence/maxBy.ts @@ -0,0 +1,16 @@ +import {error} from "../util/error.js"; + +export function maxBy(value: Iterable, transform: (it: T) => number): T { + let maxIt!: T; + let max!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val <= max) continue; + maxIt = it; + max = val; + found = true; + } + if (!found) error(); + return maxIt; +} diff --git a/src/sequence/maxByOrNull.ts b/src/sequence/maxByOrNull.ts new file mode 100644 index 0000000..33aa045 --- /dev/null +++ b/src/sequence/maxByOrNull.ts @@ -0,0 +1,14 @@ +export function maxByOrNull(value: Iterable, transform: (it: T) => number): T | null { + let maxIt!: T; + let max!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val <= max) continue; + maxIt = it; + max = val; + found = true; + } + if (!found) return null; + return maxIt; +} diff --git a/src/sequence/maxOf.ts b/src/sequence/maxOf.ts new file mode 100644 index 0000000..c7dd585 --- /dev/null +++ b/src/sequence/maxOf.ts @@ -0,0 +1,14 @@ +import {error} from "../util/error.js"; + +export function maxOf(value: Iterable, transform: (it: T) => number): number { + let max!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val <= max) continue; + max = val; + found = true; + } + if (!found) error(); + return max; +} diff --git a/src/sequence/maxOfOrNull.ts b/src/sequence/maxOfOrNull.ts new file mode 100644 index 0000000..49f165a --- /dev/null +++ b/src/sequence/maxOfOrNull.ts @@ -0,0 +1,12 @@ +export function maxOfOrNull(value: Iterable, transform: (it: T) => number): number | null { + let max!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val <= max) continue; + max = val; + found = true; + } + if (!found) return null; + return max; +} diff --git a/src/sequence/maxOrNull.ts b/src/sequence/maxOrNull.ts new file mode 100644 index 0000000..fc8b767 --- /dev/null +++ b/src/sequence/maxOrNull.ts @@ -0,0 +1,11 @@ +export function maxOrNull(value: Iterable): number | null { + let max!: number; + let found = false; + for (const it of value) { + if (found && it <= max) continue; + max = it; + found = true; + } + if (!found) return null; + return max; +} diff --git a/src/sequence/maxWith.ts b/src/sequence/maxWith.ts new file mode 100644 index 0000000..af9cda8 --- /dev/null +++ b/src/sequence/maxWith.ts @@ -0,0 +1,13 @@ +import {error} from "../util/error.js"; + +export function maxWith(value: Iterable, compare: (a: T, b: T) => number): T { + let max!: T; + let found = false; + for (const it of value) { + if (found && compare(it, max) <= 0) continue; + max = it; + found = true; + } + if (!found) error(); + return max; +} diff --git a/src/sequence/maxWithOrNull.ts b/src/sequence/maxWithOrNull.ts new file mode 100644 index 0000000..6182181 --- /dev/null +++ b/src/sequence/maxWithOrNull.ts @@ -0,0 +1,11 @@ +export function maxWithOrNull(value: Iterable, compare: (a: T, b: T) => number): T | null { + let max!: T; + let found = false; + for (const it of value) { + if (found && compare(it, max) <= 0) continue; + max = it; + found = true; + } + if (!found) return null; + return max; +} diff --git a/src/sequence/min.ts b/src/sequence/min.ts new file mode 100644 index 0000000..eebc0fc --- /dev/null +++ b/src/sequence/min.ts @@ -0,0 +1,13 @@ +import {error} from "../util/error.js"; + +export function min(value: Iterable): number { + let min!: number; + let found = false; + for (const it of value) { + if (found && it >= min) continue; + min = it; + found = true; + } + if (!found) error(); + return min; +} diff --git a/src/sequence/minBy.ts b/src/sequence/minBy.ts new file mode 100644 index 0000000..5b0be99 --- /dev/null +++ b/src/sequence/minBy.ts @@ -0,0 +1,16 @@ +import {error} from "../util/error.js"; + +export function minBy(value: Iterable, transform: (it: T) => number): T { + let minIt!: T; + let min!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val >= min) continue; + minIt = it; + min = val; + found = true; + } + if (!found) error(); + return minIt; +} diff --git a/src/sequence/minByOrNull.ts b/src/sequence/minByOrNull.ts new file mode 100644 index 0000000..e220586 --- /dev/null +++ b/src/sequence/minByOrNull.ts @@ -0,0 +1,14 @@ +export function minByOrNull(value: Iterable, transform: (it: T) => number): T | null { + let minIt!: T; + let min!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val >= min) continue; + minIt = it; + min = val; + found = true; + } + if (!found) return null; + return minIt; +} diff --git a/src/sequence/minOf.ts b/src/sequence/minOf.ts new file mode 100644 index 0000000..24bd182 --- /dev/null +++ b/src/sequence/minOf.ts @@ -0,0 +1,14 @@ +import {error} from "../util/error.js"; + +export function minOf(value: Iterable, transform: (it: T) => number): number { + let min!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val >= min) continue; + min = val; + found = true; + } + if (!found) error(); + return min; +} diff --git a/src/sequence/minOfOrNull.ts b/src/sequence/minOfOrNull.ts new file mode 100644 index 0000000..afbd75a --- /dev/null +++ b/src/sequence/minOfOrNull.ts @@ -0,0 +1,12 @@ +export function minOfOrNull(value: Iterable, transform: (it: T) => number): number | null { + let min!: number; + let found = false; + for (const it of value) { + const val = transform(it); + if (found && val >= min) continue; + min = val; + found = true; + } + if (!found) return null; + return min; +} diff --git a/src/sequence/minOrNull.ts b/src/sequence/minOrNull.ts new file mode 100644 index 0000000..906e955 --- /dev/null +++ b/src/sequence/minOrNull.ts @@ -0,0 +1,11 @@ +export function minOrNull(value: Iterable): number | null { + let min!: number; + let found = false; + for (const it of value) { + if (found && it <= min) continue; + min = it; + found = true; + } + if (!found) return null; + return min; +} diff --git a/src/sequence/minWith.ts b/src/sequence/minWith.ts new file mode 100644 index 0000000..d2904fd --- /dev/null +++ b/src/sequence/minWith.ts @@ -0,0 +1,14 @@ +import {error} from "../util/error.js"; + +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()); + } + return min; +} diff --git a/src/sequence/minWithOrNull.ts b/src/sequence/minWithOrNull.ts new file mode 100644 index 0000000..5e11acc --- /dev/null +++ b/src/sequence/minWithOrNull.ts @@ -0,0 +1,12 @@ +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()); + } + return min; +} diff --git a/src/sequence/minus.ts b/src/sequence/minus.ts new file mode 100644 index 0000000..c2f698f --- /dev/null +++ b/src/sequence/minus.ts @@ -0,0 +1,6 @@ +export function* minus(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/none.ts b/src/sequence/none.ts new file mode 100644 index 0000000..452e929 --- /dev/null +++ b/src/sequence/none.ts @@ -0,0 +1,6 @@ +export function none(value: Iterable, predicate: (it: T) => boolean): boolean { + for (const it of value) { + if (predicate(it)) return false; + } + return true; +} diff --git a/src/sequence/onEach.ts b/src/sequence/onEach.ts new file mode 100644 index 0000000..582ed6a --- /dev/null +++ b/src/sequence/onEach.ts @@ -0,0 +1,6 @@ +export function* onEach(value: Iterable, action: (it: T) => void): Iterable { + for (const it of value) { + action(it); + yield it; + } +} diff --git a/src/sequence/partition.ts b/src/sequence/partition.ts new file mode 100644 index 0000000..7ee920b --- /dev/null +++ b/src/sequence/partition.ts @@ -0,0 +1,9 @@ +export function partition(value: Iterable, predicate: (it: T) => boolean): [T[], T[]] { + const t: T[] = []; + const f: T[] = []; + for (const it of value) { + if (predicate(it)) t.push(it); + else f.push(it); + } + return [t, f]; +} diff --git a/src/sequence/plus.ts b/src/sequence/plus.ts new file mode 100644 index 0000000..ba320c1 --- /dev/null +++ b/src/sequence/plus.ts @@ -0,0 +1,8 @@ +export function* plus(value1: Iterable, value2: Iterable): Iterable { + for (const it of value1) { + yield it; + } + for (const it of value2) { + yield it; + } +} diff --git a/src/sequence/reduce.ts b/src/sequence/reduce.ts new file mode 100644 index 0000000..e19a97d --- /dev/null +++ b/src/sequence/reduce.ts @@ -0,0 +1,14 @@ +import {error} from "../util/error.js"; + +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()); + } + return acc; +} diff --git a/src/sequence/reduceOrNull.ts b/src/sequence/reduceOrNull.ts new file mode 100644 index 0000000..8455f3f --- /dev/null +++ b/src/sequence/reduceOrNull.ts @@ -0,0 +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()); + } + return acc; +} diff --git a/src/sequence/reversed.ts b/src/sequence/reversed.ts new file mode 100644 index 0000000..2a1eb88 --- /dev/null +++ b/src/sequence/reversed.ts @@ -0,0 +1,3 @@ +export function reversed(iterable: Iterable): T[] { + return [...iterable].reverse(); +} diff --git a/src/sequence/scan.ts b/src/sequence/scan.ts new file mode 100644 index 0000000..f4f2305 --- /dev/null +++ b/src/sequence/scan.ts @@ -0,0 +1,8 @@ +export function* scan(value: Iterable, initial: R, accumulate: (acc: R, it: T) => R): Iterable { + let acc = initial; + yield acc; + for (const it of value) { + acc = accumulate(acc, it); + yield acc; + } +} diff --git a/src/sequence/single.ts b/src/sequence/single.ts new file mode 100644 index 0000000..9db0d4f --- /dev/null +++ b/src/sequence/single.ts @@ -0,0 +1,14 @@ +import {error} from "../util/error.js"; + +export function single(value: Iterable, predicate?: (it: T) => boolean): T { + let last!: T; + let found = false; + for (const it of value) { + if (predicate && !predicate(it)) continue; + if (found) error(); + last = it; + found = true; + } + if (!found) error(); + return last; +} diff --git a/src/sequence/singleOrNull.ts b/src/sequence/singleOrNull.ts new file mode 100644 index 0000000..417b830 --- /dev/null +++ b/src/sequence/singleOrNull.ts @@ -0,0 +1,12 @@ +export function singleOrNull(value: Iterable, predicate?: (it: T) => boolean): T | null { + let last!: T; + let found = false; + for (const it of value) { + if (predicate && !predicate(it)) continue; + if (found) return null; + last = it; + found = true; + } + if (!found) return null; + return last; +} diff --git a/src/sequence/slice.ts b/src/sequence/slice.ts new file mode 100644 index 0000000..9cf7144 --- /dev/null +++ b/src/sequence/slice.ts @@ -0,0 +1,7 @@ +export function* slice(value: Iterable, start: number, endInclusive?: number): Iterable { + let index = 0; + for (const it of value) { + if (endInclusive !== undefined && index > endInclusive) return; + if (index++ >= start) yield it; + } +} diff --git a/src/sequence/sorted.ts b/src/sequence/sorted.ts new file mode 100644 index 0000000..ea0909e --- /dev/null +++ b/src/sequence/sorted.ts @@ -0,0 +1,3 @@ +export function sorted(iterable: Iterable): number[] { + return [...iterable].sort((a, b) => a - b); +} diff --git a/src/sequence/sortedBy.ts b/src/sequence/sortedBy.ts new file mode 100644 index 0000000..adfa7fc --- /dev/null +++ b/src/sequence/sortedBy.ts @@ -0,0 +1,3 @@ +export function sortedBy(iterable: Iterable, transform: (it: T) => number): T[] { + return [...iterable].sort((a, b) => transform(a) - transform(b)); +} diff --git a/src/sequence/sortedWith.ts b/src/sequence/sortedWith.ts new file mode 100644 index 0000000..5ffff0b --- /dev/null +++ b/src/sequence/sortedWith.ts @@ -0,0 +1,3 @@ +export function sortedWith(iterable: Iterable, compare: (a: T, b: T) => number): T[] { + return [...iterable].sort(compare); +} diff --git a/src/sequence/sum.ts b/src/sequence/sum.ts new file mode 100644 index 0000000..d4baa12 --- /dev/null +++ b/src/sequence/sum.ts @@ -0,0 +1,7 @@ +export function sum(value: Iterable): number { + let sum = 0; + for (const it of value) { + sum += it; + } + return sum; +} diff --git a/src/sequence/sumBy.ts b/src/sequence/sumBy.ts new file mode 100644 index 0000000..feabcf2 --- /dev/null +++ b/src/sequence/sumBy.ts @@ -0,0 +1,7 @@ +export function sumBy(value: Iterable, transform: (it: T) => number): number { + let sum = 0; + for (const it of value) { + sum += transform(it); + } + return sum; +} diff --git a/src/sequence/take.ts b/src/sequence/take.ts new file mode 100644 index 0000000..0ab257f --- /dev/null +++ b/src/sequence/take.ts @@ -0,0 +1,7 @@ +export function* take(value: Iterable, count: number): Iterable { + let taken = 0; + for (const it of value) { + if (taken++ === count) return; + yield it; + } +} diff --git a/src/sequence/takeUntil.ts b/src/sequence/takeUntil.ts new file mode 100644 index 0000000..2fbcf89 --- /dev/null +++ b/src/sequence/takeUntil.ts @@ -0,0 +1,6 @@ +export function* takeUntil(value: Iterable, predicate: (it: T) => boolean): Iterable { + for (const it of value) { + if (predicate(it)) return; + yield it; + } +} diff --git a/src/sequence/takeWhile.ts b/src/sequence/takeWhile.ts new file mode 100644 index 0000000..9f5c7f2 --- /dev/null +++ b/src/sequence/takeWhile.ts @@ -0,0 +1,6 @@ +export function* takeWhile(value: Iterable, predicate: (it: T) => boolean): Iterable { + for (const it of value) { + if (!predicate(it)) return; + yield it; + } +} diff --git a/src/sequence/unzip.ts b/src/sequence/unzip.ts new file mode 100644 index 0000000..2b9cb2f --- /dev/null +++ b/src/sequence/unzip.ts @@ -0,0 +1,12 @@ +export function unzip(value: Iterable<[A, B]>): [A[], B[]] { + const a: A[] = []; + const b: B[] = []; + 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()); + } + return [a, b]; +} diff --git a/src/sequence/windowed.ts b/src/sequence/windowed.ts new file mode 100644 index 0000000..a6cf0c7 --- /dev/null +++ b/src/sequence/windowed.ts @@ -0,0 +1,15 @@ +export function* windowed(value: Iterable, size: number, step: number = 1, partial: boolean = false): Iterable { + const buffer: T[] = []; + for (const it of value) { + buffer.push(it); + if (buffer.length === size) { + yield [...buffer]; + buffer.splice(0, step); + } + } + if (!partial) return; + while (buffer.length !== 0) { + yield [...buffer]; + buffer.splice(0, step); + } +} diff --git a/src/sequence/withIndex.ts b/src/sequence/withIndex.ts new file mode 100644 index 0000000..ae705f9 --- /dev/null +++ b/src/sequence/withIndex.ts @@ -0,0 +1,6 @@ +export function* withIndex(value: Iterable): Iterable<[number, T]> { + let index = 0; + for (const it of value) { + yield [index++, it]; + } +} diff --git a/src/sequence/zip.ts b/src/sequence/zip.ts new file mode 100644 index 0000000..9d80876 --- /dev/null +++ b/src/sequence/zip.ts @@ -0,0 +1,9 @@ +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]; + } +} diff --git a/src/util/error.ts b/src/util/error.ts new file mode 100644 index 0000000..fc4dbc5 --- /dev/null +++ b/src/util/error.ts @@ -0,0 +1,3 @@ +export function error(message?: string): never { + throw Error(message); +} diff --git a/tsconfig.json b/tsconfig.json index 8c1d21f..166eeca 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "moduleResolution": "NodeNext", "module": "NodeNext", + "moduleResolution": "NodeNext", "target": "ESNext", "rootDir": "./src/", "outDir": "./dist/",