Composable functional Iterable<T> helpers with no runtime dependencies. They work with arrays,
sets, generators and any other iterable, and transforms stay lazy wherever possible.
- Node.js
>= 22.18 - ESM (
import, notrequire)
npm install iterama
import { concat, map, take } from 'iterama'
const result = [...take(3)(map((x: number) => x * 2)(concat([1, 2, 3], [4, 5, 6])))]
// [2, 4, 6]<T> (...iterables: Iterable<T>[]) => Iterable<T>
import { concat } from 'iterama'
const data0 = [1, 2, 3]
const data1 = [4, 5, 6]
const result = [...concat(data0, data1)]
// [1, 2, 3, 4, 5, 6]<T> (iterable: Iterable<T>) => Iterable<T>
Drops consecutive duplicates.
import { distinct } from 'iterama'
const result = [...distinct([1, 1, 3, 3, 4, 3])]
// [1, 3, 4, 3]<T> (predicate: (value: T) => boolean) => (iterable: Iterable<T>) => Iterable<T>
import { filter } from 'iterama'
const isEven = (x: number) => x % 2 === 0
const result = [...filter(isEven)([1, 2, 3, 4])]
// [2, 4]<T> (predicate: (value: T, index: number, iterable: Iterable<T>) => boolean) => (iterable: Iterable<T>) => Iterable<T>
import { filterEx } from 'iterama'
const isEvenIndex = (_value: string, i: number) => i % 2 === 0
const result = [...filterEx(isEvenIndex)(['a', 'b', 'c', 'd'])]
// ['a', 'c']<T> (iterable: Iterable<T>) => Generator<T>
import { iterate } from 'iterama'
const result = [...iterate([1, 2, 3, 4, 5])]
// [1, 2, 3, 4, 5](maxLength: number) => <T> (iterable: Iterable<T>) => number
Counts the values of an iterable, stopping after maxLength.
import { length } from 'iterama'
const result = length(Number.MAX_SAFE_INTEGER)([1, 2, 3, 4, 5])
// 5<T, R> (transform: (value: T) => R) => (iterable: Iterable<T>) => Iterable<R>
import { map } from 'iterama'
const mult2 = (x: number) => x * 2
const result = [...map(mult2)([1, 2, 3, 4])]
// [2, 4, 6, 8]<T, R> (transform: (value: T, index: number, iterable: Iterable<T>) => R) => (iterable: Iterable<T>) => Iterable<R>
import { mapEx } from 'iterama'
const addIndex = (x: number, i: number) => x + i
const result = [...mapEx(addIndex)([1, 2, 3, 4])]
// [1, 3, 5, 7](length: number) => Iterable<number>
import { range } from 'iterama'
const result = [...range(4)]
// [0, 1, 2, 3]<T, R> (reducer: (accumulator?: R, value?: T) => R) => (iterable: Iterable<T>) => Iterable<R>
Folds an iterable into a single value and yields it once. The reducer is called without arguments to create the initial state, then once per value.
import { reduce } from 'iterama'
// redux-like reducer
const reducer = (state = 0, value = 0) => state + value
const result = [...reduce(reducer)([1, 2, 3, 4])]
// [10]<T, R> (reducer: (accumulator: R, value: T, index: number, iterable: Iterable<T>) => R, initial: R) => (iterable: Iterable<T>) => Iterable<R>
import { reduceEx } from 'iterama'
// Array.prototype.reduce-like reducer
const reducer = (acc: number, value: number) => acc + value
const result = [...reduceEx(reducer, 0)([1, 2, 3, 4])]
// [10]<T> (iterator: Iterator<T | Promise<T>>) => Promise<void>
Drives a synchronous generator whose yields are promises, awaiting every yielded value and passing it back into the iterator.
import { resolve } from 'iterama'
function* source() {
const a = yield Promise.resolve(1)
const b = yield Promise.resolve(a + 1)
return b
}
await resolve(source())<T, R> (reducer: (accumulator?: R, value?: T) => R) => (iterable: Iterable<T>) => Iterable<R>
Emits the accumulator after every value. The reducer is called without arguments to create the seed state.
import { scan } from 'iterama'
// redux-like reducer
const reducer = (state = 0, value = 0) => state + value
const result = [...scan(reducer)([1, 2, 3, 4])]
// [1, 3, 6, 10]<T, R> (reducer: (accumulator: R, value: T, index: number, iterable: Iterable<T>) => R, initial: R) => (iterable: Iterable<T>) => Iterable<R>
import { scanEx } from 'iterama'
const reducer = (acc: number, value: number) => acc + value
const result = [...scanEx(reducer, 0)([1, 2, 3, 4])]
// [1, 3, 6, 10](count: number) => <T> (iterable: Iterable<T>) => Iterable<T>
A negative count skips the last |count| values. A fractional count is truncated, like
Array.prototype.slice; NaN behaves like 0 and -Infinity skips everything.
import { skip } from 'iterama'
// skip the first 2 values
const result0 = [...skip(2)([1, 2, 3, 4, 5, 6])]
// [3, 4, 5, 6]
// skip the last 2 values
const result1 = [...skip(-2)([1, 2, 3, 4, 5, 6])]
// [1, 2, 3, 4](from?: number, to?: number) => <T> (iterable: Iterable<T>) => Iterable<T>
Negative bounds are counted from the end of the iterable.
import { slice } from 'iterama'
// skip 1, take 2
const r0 = [...slice(1, 2)([1, 2, 3, 4, 5])]
// [2, 3]
// skip until 2 from the end, take 1
const r1 = [...slice(-2, 1)([1, 2, 3, 4, 5])]
// [4]
// don't skip, drop the last 2
const r2 = [...slice(0, -2)([1, 2, 3, 4, 5])]
// [1, 2, 3]
// skip 2, take the rest
const r3 = [...slice(2)([1, 2, 3, 4, 5])]
// [3, 4, 5]
// skip until 2 from the end, take the rest
const r4 = [...slice(-2)([1, 2, 3, 4, 5])]
// [4, 5]
// don't skip, take all
const r5 = [...slice()([1, 2, 3, 4, 5])]
// [1, 2, 3, 4, 5]<T> (value: T) => (iterable: Iterable<T>) => Iterable<T>
import { startWith } from 'iterama'
const r = [...startWith(0)([1, 2, 3])]
// [0, 1, 2, 3](count: number) => <T> (iterable: Iterable<T>) => Iterable<T>
A negative count takes the last |count| values. A fractional count is truncated, like
Array.prototype.slice; NaN behaves like 0 and -Infinity takes the whole iterable.
import { take } from 'iterama'
// take the first 2 values
const r0 = [...take(2)([1, 2, 3, 4, 5])]
// [1, 2]
// take the last 2 values
const r1 = [...take(-2)([1, 2, 3, 4, 5])]
// [4, 5]<T> (iterable: Iterable<T>) => Iterable<T>
Drops all duplicates, keeping the first occurrence of every value.
import { unique } from 'iterama'
const r = [...unique([1, 1, 3, 4, 3])]
// [1, 3, 4]<A, B> (it0: Iterable<A>, it1: Iterable<B>): Iterable<[A, B]>
<A, B, C> (it0: Iterable<A>, it1: Iterable<B>, it2: Iterable<C>): Iterable<[A, B, C]>
<A, B, C, D> (it0: Iterable<A>, it1: Iterable<B>, it2: Iterable<C>, it3: Iterable<D>): Iterable<[A, B, C, D]>Zips iterables together, stopping at the shortest one.
import { zip } from 'iterama'
const r = [...zip([1, 2, 3, 4, 5, 6], ['a', 'b', 'c', 'd'])]
// [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd']]| Script | Description |
|---|---|
npm test |
Runs the test suite with the built-in Node.js test runner |
npm run typecheck |
Type-checks sources and tests with TypeScript |
npm run lint |
Lints with oxlint |
npm run format |
Formats with oxfmt |
npm run build |
Emits ESM and type declarations to dist |
Source files are executed directly by Node.js through its built-in TypeScript type stripping, so there is no build step or transform in the test loop.
MIT