Iterable.ts
Iterable.ts overview
Section titled “Iterable.ts overview”Works with JavaScript values that implement [Symbol.iterator].
Iterables include arrays, strings, generators, sets, and custom lazy sequences. The helpers in this module let code transform, search, group, and fold iterable values while preserving the input as an iterable instead of forcing an array first.
Since v2.0.0
Exports Grouped by Category
Section titled “Exports Grouped by Category”- combining
- constructors
- converting
- elements
- filtering
- folding
- getters
- grouping
- guards
- mapping
- sequencing
- splitting
- zipping
combining
Section titled “combining”append
Section titled “append”Appends an element to the end of an Iterable, creating a new Iterable.
When to use
Use to add one element after all elements of an iterable while keeping the
result as a lazy Iterable.
Details
The result yields every element from self first, then yields last after
self is exhausted.
Gotchas
If self is infinite or never completes, the appended element is never
reached.
Example (Appending an element)
import { Iterable } from "effect"
const numbers = [1, 2, 3]const withFour = Iterable.append(numbers, 4)console.log(Array.from(withFour)) // [1, 2, 3, 4]
// Chain multiple appendsconst result = Iterable.append(Iterable.append([1, 2], 3), 4)console.log(Array.from(result)) // [1, 2, 3, 4]See
prependfor adding one element before the existing elementsappendAllfor appending all elements from another iterable
Signature
declare const append: { <B>(last: B): <A>(self: Iterable<A>) => Iterable<A | B> <A, B>(self: Iterable<A>, last: B): Iterable<A | B>}Since v2.0.0
appendAll
Section titled “appendAll”Concatenates two iterables, combining their elements.
When to use
Use to lazily concatenate two iterables while preserving order, yielding all
elements from self before that.
Details
The result is lazy. The iterator for that is not created or read until
self is exhausted.
Gotchas
If self is infinite or never completes, that is never reached.
Example (Concatenating iterables)
import { Iterable } from "effect"
const first = [1, 2, 3]const second = [4, 5, 6]const combined = Iterable.appendAll(first, second)console.log(Array.from(combined)) // [1, 2, 3, 4, 5, 6]
// Works with different iterable typesconst numbers = [1, 2]const letters = "abc"const mixed = Iterable.appendAll(numbers, letters)console.log(Array.from(mixed)) // [1, 2, "a", "b", "c"]
// Lazy evaluation - only consumes what's neededconst infinite = Iterable.range(1)const finite = [0, -1, -2]const result = Iterable.take(Iterable.appendAll(finite, infinite), 5)console.log(Array.from(result)) // [0, -1, -2, 1, 2]See
appendfor appending one value instead of another iterableprependAllfor yielding another iterable beforeself
Signature
declare const appendAll: { <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<A | B> <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>}Since v2.0.0
intersperse
Section titled “intersperse”Places a separator between members of an Iterable.
When to use
Use to lazily insert a separator between adjacent values.
Details
If the input is a non-empty array, the result is also a non-empty array.
Example (Interspersing separators)
import { Iterable } from "effect"
// Join numbers with separatorconst numbers = [1, 2, 3, 4]const withCommas = Iterable.intersperse(numbers, ",")console.log(Array.from(withCommas)) // [1, ",", 2, ",", 3, ",", 4]
// Join words with spacesconst words = ["hello", "world", "from", "effect"]const sentence = Iterable.intersperse(words, " ")console.log(Array.from(sentence).join("")) // "hello world from effect"
// Empty iterable remains emptyconst empty = Iterable.empty<string>()const stillEmpty = Iterable.intersperse(empty, "-")console.log(Array.from(stillEmpty)) // []
// Single element has no separators addedconst single = [42]const noSeparator = Iterable.intersperse(single, "|")console.log(Array.from(noSeparator)) // [42]
// Build CSS-like stringsconst styles = ["color: red", "font-size: 14px", "margin: 10px"]const css = Iterable.intersperse(styles, "; ")console.log(Array.from(css).join("")) // "color: red; font-size: 14px; margin: 10px"Signature
declare const intersperse: { <B>(middle: B): <A>(self: Iterable<A>) => Iterable<A | B> <A, B>(self: Iterable<A>, middle: B): Iterable<A | B>}Since v2.0.0
prepend
Section titled “prepend”Prepends an element to the front of an Iterable, creating a new Iterable.
Example (Prepending an element)
import { Iterable } from "effect"
const numbers = [2, 3, 4]const withOne = Iterable.prepend(numbers, 1)console.log(Array.from(withOne)) // [1, 2, 3, 4]
// Works with any iterableconst letters = "abc"const withZ = Iterable.prepend(letters, "z")console.log(Array.from(withZ)) // ["z", "a", "b", "c"]Signature
declare const prepend: { <B>(head: B): <A>(self: Iterable<A>) => Iterable<A | B> <A, B>(self: Iterable<A>, head: B): Iterable<A | B>}Since v2.0.0
prependAll
Section titled “prependAll”Prepends the specified prefix iterable to the beginning of the specified iterable.
Example (Prepending another iterable)
import { Iterable } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Array.from(Iterable.prependAll([1, 2], ["a", "b"])), ["a", "b", 1, 2])Signature
declare const prependAll: { <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<A | B> <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>}Since v2.0.0
constructors
Section titled “constructors”Creates an empty iterable that yields no elements.
When to use
Use when you need an empty iterable as a typed “no data” value or a base case for iterable operations.
Example (Creating an empty iterable)
import { Iterable } from "effect"
const empty = Iterable.empty<string>()console.log(Array.from(empty)) // []console.log(Iterable.isEmpty(empty)) // true
// Useful as base case for reductionsconst hasData = trueconst result = hasData ? Iterable.range(1, 5) : Iterable.empty<number>()Signature
declare const empty: <A = never>() => Iterable<A>Since v2.0.0
forever
Section titled “forever”Repeats an iterable without an upper bound.
When to use
Use to cycle a reusable iterable without an upper bound when a downstream consumer controls how many values are taken.
Gotchas
The returned iterable is lazy and should usually be bounded with take or
another terminating consumer before materializing it.
See
repeatfor repeating an iterable a specific number of timestakefor bounding the unbounded result before materializing it
Signature
declare const forever: <A>(self: Iterable<A>) => Iterable<A>Since v4.0.0
makeBy
Section titled “makeBy”Creates an iterable by applying a function to consecutive integers.
Details
The function is called with each index starting from 0. If no length is
specified, the iterable is infinite. This is useful for generating
sequences, patterns, or any indexed data.
Example (Generating values by index)
import { Iterable } from "effect"
// Generate first 5 even numbersconst evens = Iterable.makeBy((n) => n * 2, { length: 5 })console.log(Array.from(evens)) // [0, 2, 4, 6, 8]
// Generate squaresconst squares = Iterable.makeBy((n) => n * n, { length: 4 })console.log(Array.from(squares)) // [0, 1, 4, 9]
// Infinite sequence (be careful when consuming!)const naturals = Iterable.makeBy((n) => n)const first10 = Iterable.take(naturals, 10)console.log(Array.from(first10)) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]Signature
declare const makeBy: <A>(f: (i: number) => A, options?: { readonly length?: number }) => Iterable<A>Since v2.0.0
Creates an iterable containing a single element.
When to use
Use to wrap a single value in an iterable context so it can be combined with other iterable operations.
Example (Wrapping a single value)
import { Iterable } from "effect"
const single = Iterable.of(42)console.log(Array.from(single)) // [42]
// Useful for creating homogeneous sequencesconst sequences = [Iterable.of("hello"), Iterable.range(1, 3), Iterable.empty<string>()]
// Can be used with flatMap for conditional inclusionconst numbers = [1, 2, 3, 4, 5]const evensOnly = Iterable.flatMap(numbers, (n) => (n % 2 === 0 ? Iterable.of(n) : Iterable.empty()))console.log(Array.from(evensOnly)) // [2, 4]Signature
declare const of: <A>(a: A) => Iterable<A>Since v2.0.0
Returns an iterable of integers starting at start and increasing by 1.
Details
When end is provided and start <= end, both endpoints are included. When
end is omitted, the iterable is unbounded. When start > end, the
iterable contains only start.
Example (Creating a range)
import { Iterable } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Array.from(Iterable.range(1, 3)), [1, 2, 3])Signature
declare const range: (start: number, end?: number) => Iterable<number>Since v2.0.0
repeat
Section titled “repeat”Repeats an iterable n times, yielding the full contents of self for each
repetition.
When to use
Use to repeat an iterable’s contents a specific number of times.
Details
The result is lazy. Each repetition obtains a new iterator from self.
See
foreverfor repeating without an upper boundreplicatefor repeating a single value
Signature
declare const repeat: { (n: number): <A>(self: Iterable<A>) => Iterable<A> <A>(self: Iterable<A>, n: number): Iterable<A>}Since v4.0.0
replicate
Section titled “replicate”Returns a Iterable containing a value repeated the specified number of times.
Details
n is normalized to an integer greater than or equal to 1.
Example (Repeating a value)
import { Iterable } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Array.from(Iterable.replicate("a", 3)), ["a", "a", "a"])Signature
declare const replicate: { (n: number): <A>(a: A) => Iterable<A>; <A>(a: A, n: number): Iterable<A> }Since v2.0.0
unfold
Section titled “unfold”Generates an iterable by repeatedly applying a function that produces the next element and state.
Details
This is useful for creating iterables from a generating function that
maintains state. The function should return Option.some([value, nextState])
to continue or Option.none() to stop.
Example (Unfolding state into values)
import { Iterable, Option } from "effect"
// Generate Fibonacci sequenceconst fibonacci = Iterable.unfold([0, 1], ([a, b]) => Option.some([a, [b, a + b]]))const first10Fib = Iterable.take(fibonacci, 10)console.log(Array.from(first10Fib)) // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
// Generate powers of 2 up to a limitconst powersOf2 = Iterable.unfold(1, (n) => (n <= 1000 ? Option.some([n, n * 2]) : Option.none()))console.log(Array.from(powersOf2)) // [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
// Generate countdownconst countdown = Iterable.unfold(5, (n) => (n > 0 ? Option.some([n, n - 1]) : Option.none()))console.log(Array.from(countdown)) // [5, 4, 3, 2, 1]
// Generate collatz sequenceconst collatz = Iterable.unfold(7, (n) => { if (n === 1) return Option.none() const next = n % 2 === 0 ? n / 2 : n * 3 + 1 return Option.some([n, next])})console.log(Array.from(collatz)) // [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2]Signature
declare const unfold: <B, A>(b: B, f: (b: B) => Option<readonly [A, B]>) => Iterable<A>Since v2.0.0
converting
Section titled “converting”fromRecord
Section titled “fromRecord”Takes a record and returns an Iterable of tuples containing its keys and values.
Example (Converting a record to entries)
import { Iterable } from "effect"import * as assert from "node:assert"
const x = { a: 1, b: 2, c: 3 }assert.deepStrictEqual(Array.from(Iterable.fromRecord(x)), [ ["a", 1], ["b", 2], ["c", 3]])Signature
declare const fromRecord: <K extends string, A>(self: Readonly<Record<K, A>>) => Iterable<[K, A]>Since v2.0.0
elements
Section titled “elements”cartesian
Section titled “cartesian”Zips this Iterable crosswise with the specified Iterable.
Example (Generating cartesian pairs)
import { Iterable } from "effect"
// All pairs of numbers and lettersconst numbers = [1, 2, 3]const letters = ["a", "b"]const pairs = Iterable.cartesian(numbers, letters)console.log(Array.from(pairs))// [[1, "a"], [1, "b"], [2, "a"], [2, "b"], [3, "a"], [3, "b"]]
// Generate coordinate gridconst x = [0, 1, 2]const y = [0, 1]const grid = Iterable.cartesian(x, y)console.log(Array.from(grid))// [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]
// All combinations for testingconst browsers = ["chrome", "firefox"]const devices = ["desktop", "mobile", "tablet"]const testMatrix = Iterable.cartesian(browsers, devices)console.log(Array.from(testMatrix))// [// ["chrome", "desktop"], ["chrome", "mobile"], ["chrome", "tablet"],// ["firefox", "desktop"], ["firefox", "mobile"], ["firefox", "tablet"]// ]
// Empty iterable results in empty cartesian productconst empty = Iterable.empty<number>()const withEmpty = Iterable.cartesian([1, 2], empty)console.log(Array.from(withEmpty)) // []Signature
declare const cartesian: { <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<[A, B]> <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]>}Since v2.0.0
cartesianWith
Section titled “cartesianWith”Zips this Iterable crosswise with the specified Iterable using the specified combiner.
Example (Combining cartesian products)
import { Iterable } from "effect"
// Create coordinate pairsconst xs = [1, 2]const ys = ["a", "b", "c"]const coordinates = Iterable.cartesianWith(xs, ys, (x, y) => `(${x},${y})`)console.log(Array.from(coordinates)) // ["(1,a)", "(1,b)", "(1,c)", "(2,a)", "(2,b)", "(2,c)"]
// Generate all combinations of optionsconst sizes = ["S", "M", "L"]const colors = ["red", "blue"]const products = Iterable.cartesianWith(sizes, colors, (size, color) => ({ size, color }))console.log(Array.from(products))// [// { size: "S", color: "red" }, { size: "S", color: "blue" },// { size: "M", color: "red" }, { size: "M", color: "blue" },// { size: "L", color: "red" }, { size: "L", color: "blue" }// ]
// Mathematical operations on all pairsconst a = [1, 2, 3]const b = [10, 20]const mathProducts = Iterable.cartesianWith(a, b, (x, y) => x * y)console.log(Array.from(mathProducts)) // [10, 20, 20, 40, 30, 60]
// Create test data combinationsconst userTypes = ["admin", "user"]const features = ["read", "write", "delete"]const testCases = Iterable.cartesianWith(userTypes, features, (user, feature) => `${user}_can_${feature}`)console.log(Array.from(testCases))// ["admin_can_read", "admin_can_write", "admin_can_delete", "user_can_read", "user_can_write", "user_can_delete"]Signature
declare const cartesianWith: { <A, B, C>(that: Iterable<B>, f: (a: A, b: B) => C): (self: Iterable<A>) => Iterable<C> <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C>}Since v2.0.0
contains
Section titled “contains”Checks whether an iterable contains a value using Effect’s default Equal
equivalence.
Details
Can be called as contains(self, value) or curried as
contains(value)(self).
Example (Checking membership)
import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]console.log(Iterable.contains(numbers, 3)) // trueconsole.log(Iterable.contains(numbers, 6)) // false
const letters = "hello"console.log(Iterable.contains(letters, "l")) // trueconsole.log(Iterable.contains(letters, "x")) // false
// Works with any iterableconst range = Iterable.range(1, 100)console.log(Iterable.contains(range, 50)) // trueconsole.log(Iterable.contains(range, 150)) // false
// Curried versionconst containsThree = Iterable.contains(3)console.log(containsThree([1, 2, 3])) // trueconsole.log(containsThree([4, 5, 6])) // falseSignature
declare const contains: { <A>(a: A): (self: Iterable<A>) => boolean; <A>(self: Iterable<A>, a: A): boolean }Since v2.0.0
containsWith
Section titled “containsWith”Returns a function that checks if an Iterable contains a given value using a provided isEquivalent function.
Example (Checking membership with custom equivalence)
import { Iterable } from "effect"
// Custom equivalence for objectsconst byId = (a: { id: number }, b: { id: number }) => a.id === b.idconst containsById = Iterable.containsWith(byId)
const users = [{ id: 1 }, { id: 2 }]const hasUser1 = containsById(users, { id: 1 })console.log(hasUser1) // true (same id)
// Case-insensitive string comparisonconst caseInsensitive = (a: string, b: string) => a.toLowerCase() === b.toLowerCase()const containsCaseInsensitive = Iterable.containsWith(caseInsensitive)
const words = ["Hello", "World"]const hasHello = containsCaseInsensitive(words, "hello")console.log(hasHello) // true
// Approximate number comparisonconst approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1const containsApprox = Iterable.containsWith(approxEqual)
const values = [1.0, 2.0, 3.0]const hasAlmostTwo = containsApprox(values, 2.05)console.log(hasAlmostTwo) // trueSignature
declare const containsWith: <A>(isEquivalent: (self: A, that: A) => boolean) => { (a: A): (self: Iterable<A>) => boolean (self: Iterable<A>, a: A): boolean}Since v2.0.0
findFirst
Section titled “findFirst”Returns the first element that satisfies the specified
predicate, or None if no such element exists.
Example (Finding the first match)
import { Iterable, Option } from "effect"
const numbers = [1, 3, 4, 6, 8]const firstEven = Iterable.findFirst(numbers, (x) => x % 2 === 0)console.log(firstEven) // Option.some(4)
const firstGreaterThan10 = Iterable.findFirst(numbers, (x) => x > 10)console.log(firstGreaterThan10) // Option.none()
// With indexconst letters = ["a", "b", "c", "d"]const atEvenIndex = Iterable.findFirst(letters, (_, i) => i % 2 === 0)console.log(atEvenIndex) // Option.some("a")
// Type refinementconst mixed: Array<string | number> = [1, "hello", 2, "world"]const firstString = Iterable.findFirst(mixed, (x): x is string => typeof x === "string")console.log(firstString) // Option.some("hello")
// Transform during searchconst findSquareRoot = Iterable.findFirst([1, 4, 9, 16], (x) => { const sqrt = Math.sqrt(x) return Number.isInteger(sqrt) ? Option.some(sqrt) : Option.none()})console.log(findSquareRoot) // Option.some(1)Signature
declare const findFirst: { <A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B> <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Option<B> <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Option<A> <A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B> <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B> <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>}Since v2.0.0
findLast
Section titled “findLast”Finds the last element for which a predicate holds.
Example (Finding the last match)
import { Iterable } from "effect"
const numbers = [1, 3, 4, 6, 8, 2]const lastEven = Iterable.findLast(numbers, (x) => x % 2 === 0)console.log(lastEven) // Option.some(2)
const lastGreaterThan10 = Iterable.findLast(numbers, (x) => x > 10)console.log(lastGreaterThan10) // Option.none()
// With indexconst letters = ["a", "b", "c", "d", "e"]const lastAtEvenIndex = Iterable.findLast(letters, (_, i) => i % 2 === 0)console.log(lastAtEvenIndex) // Option.some("e") (index 4)
// Type refinementconst mixed: Array<string | number> = [1, "hello", 2, "world", 3]const lastString = Iterable.findLast(mixed, (x): x is string => typeof x === "string")console.log(lastString) // Option.some("world")Signature
declare const findLast: { <A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B> <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Option<B> <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Option<A> <A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B> <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B> <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>}Since v2.0.0
forEach
Section titled “forEach”Iterates over the Iterable, applying f to each element.
Example (Iterating with side effects)
import { Iterable } from "effect"
// Print each elementconst numbers = [1, 2, 3, 4, 5]Iterable.forEach(numbers, (n) => console.log(n))// Prints: 1, 2, 3, 4, 5
// Use index in the callbackconst letters = ["a", "b", "c"]Iterable.forEach(letters, (letter, i) => { console.log(`${i}: ${letter}`)})// Prints: "0: a", "1: b", "2: c"
// Side effects with any iterableconst results: Array<number> = []Iterable.forEach(Iterable.range(1, 5), (n) => { results.push(n * n)})console.log(results) // [1, 4, 9, 16, 25]
// Process in chunksconst data = Iterable.chunksOf([1, 2, 3, 4, 5, 6], 2)Iterable.forEach(data, (chunk) => { console.log(`Processing chunk: ${Array.from(chunk)}`)})// Prints: "Processing chunk: 1,2", "Processing chunk: 3,4", "Processing chunk: 5,6"Signature
declare const forEach: { <A>(f: (a: A, i: number) => void): (self: Iterable<A>) => void <A>(self: Iterable<A>, f: (a: A, i: number) => void): void}Since v2.0.0
Checks whether a predicate holds true for some Iterable element.
Example (Checking whether some element matches)
import { Iterable } from "effect"
const numbers = [1, 3, 5, 7, 8]const hasEven = Iterable.some(numbers, (x) => x % 2 === 0)console.log(hasEven) // true (because of 8)
const allOdd = [1, 3, 5, 7]const hasEvenInAllOdd = Iterable.some(allOdd, (x) => x % 2 === 0)console.log(hasEvenInAllOdd) // false
// With indexconst letters = ["a", "b", "c"]const hasElementAtIndex2 = Iterable.some(letters, (_, i) => i === 2)console.log(hasElementAtIndex2) // true
// Early termination - stops at first matchconst infiniteOdds = Iterable.filter(Iterable.range(1), (x) => x % 2 === 1)const hasEvenInInfiniteOdds = Iterable.some(Iterable.take(infiniteOdds, 1000), (x) => x % 2 === 0)console.log(hasEvenInInfiniteOdds) // false (quickly, doesn't check all 1000)
// Type guard usageconst mixed: Array<string | number> = [1, 2, "hello"]const hasString = Iterable.some(mixed, (x): x is string => typeof x === "string")console.log(hasString) // trueSignature
declare const some: { <A>(predicate: (a: A, i: number) => boolean): (self: Iterable<A>) => boolean <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): boolean}Since v2.0.0
filtering
Section titled “filtering”dedupeAdjacent
Section titled “dedupeAdjacent”Deduplicates adjacent elements that are identical.
Example (Deduplicating adjacent elements)
import { Iterable } from "effect"
// Remove adjacent duplicate numbersconst numbers = [1, 1, 2, 2, 2, 3, 1, 1]const deduped = Iterable.dedupeAdjacent(numbers)console.log(Array.from(deduped)) // [1, 2, 3, 1]
// Remove adjacent duplicate charactersconst letters = "aabbccaa"const dedupedLetters = Iterable.dedupeAdjacent(letters)console.log(Array.from(dedupedLetters)) // ["a", "b", "c", "a"]
// Works with objects using deep equalityconst objects = [{ type: "A" }, { type: "A" }, { type: "B" }, { type: "B" }, { type: "A" }]const dedupedObjects = Iterable.dedupeAdjacent(objects)console.log(Array.from(dedupedObjects).map((o) => o.type)) // ["A", "B", "A"]
// Clean up streaming dataconst sensorData = [100, 100, 100, 101, 101, 102, 102, 102, 100]const cleanedData = Iterable.dedupeAdjacent(sensorData)console.log(Array.from(cleanedData)) // [100, 101, 102, 100]Signature
declare const dedupeAdjacent: <A>(self: Iterable<A>) => Iterable<A>Since v2.0.0
dedupeAdjacentWith
Section titled “dedupeAdjacentWith”Deduplicates adjacent elements that are identical using the provided isEquivalent function.
Example (Deduplicating adjacent elements with custom equivalence)
import { Iterable } from "effect"
// Remove adjacent duplicates with custom equalityconst numbers = [1, 1, 2, 2, 3, 1, 1]const dedupedNumbers = Iterable.dedupeAdjacentWith(numbers, (a, b) => a === b)console.log(Array.from(dedupedNumbers)) // [1, 2, 3, 1]
// Case-insensitive deduplicationconst words = ["Hello", "HELLO", "world", "World", "test"]const caseInsensitive = (a: string, b: string) => a.toLowerCase() === b.toLowerCase()const dedupedWords = Iterable.dedupeAdjacentWith(words, caseInsensitive)console.log(Array.from(dedupedWords)) // ["Hello", "world", "test"]
// Deduplication by object propertyconst users = [ { id: 1, name: "Alice" }, { id: 1, name: "Alice Updated" }, // different name, same id { id: 2, name: "Bob" }, { id: 2, name: "Bob" }, { id: 3, name: "Charlie" }]const byId = (a: (typeof users)[0], b: (typeof users)[0]) => a.id === b.idconst dedupedUsers = Iterable.dedupeAdjacentWith(users, byId)console.log(Array.from(dedupedUsers).map((u) => u.id)) // [1, 2, 3]
// Approximate numeric equalityconst floats = [1.0, 1.01, 1.02, 2.0, 2.01, 3.0]const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1const dedupedFloats = Iterable.dedupeAdjacentWith(floats, approxEqual)console.log(Array.from(dedupedFloats)) // [1.0, 2.0, 3.0]Signature
declare const dedupeAdjacentWith: { <A>(isEquivalent: (self: A, that: A) => boolean): (self: Iterable<A>) => Iterable<A> <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<A>}Since v2.0.0
filter
Section titled “filter”Filters an iterable to only include elements that match a predicate.
Details
This function creates a new iterable containing only the elements for which the predicate function returns true. Like map, this operation is lazy and elements are only tested when the iterable is consumed.
Example (Filtering elements)
import { Iterable } from "effect"
// Filter even numbersconst numbers = [1, 2, 3, 4, 5, 6]const evens = Iterable.filter(numbers, (x) => x % 2 === 0)console.log(Array.from(evens)) // [2, 4, 6]
// Filter with indexconst items = ["a", "b", "c", "d"]const oddPositions = Iterable.filter(items, (_, i) => i % 2 === 1)console.log(Array.from(oddPositions)) // ["b", "d"]
// Type refinementconst mixed: Array<string | number> = ["hello", 42, "world", 100]const onlyStrings = Iterable.filter(mixed, (x): x is string => typeof x === "string")console.log(Array.from(onlyStrings)) // ["hello", "world"] (typed as string[])
// Combine with mapconst processed = Iterable.map( Iterable.filter([1, 2, 3, 4, 5], (x) => x > 2), (x) => x * 10)console.log(Array.from(processed)) // [30, 40, 50]Signature
declare const filter: { <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Iterable<B> <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Iterable<A> <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Iterable<B> <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A>}Since v2.0.0
filterMap
Section titled “filterMap”Transforms elements of an iterable using a function that returns a Result, keeping only successful values.
Details
This combines mapping and filtering in a single operation. The function is
applied to each element, and only elements that result in Result.succeed
are included in the result.
Example (Filtering and transforming Result values)
import { Iterable, Result } from "effect"
// Parse strings to numbers, keeping only valid onesconst strings = ["1", "2", "invalid", "4", "not-a-number"]const numbers = Iterable.filterMap(strings, (s) => { const num = parseInt(s) return isNaN(num) ? Result.failVoid : Result.succeed(num)})console.log(Array.from(numbers)) // [1, 2, 4]
// Extract specific properties from objectsconst users = [ { name: "Alice", age: 25, email: "alice@example.com" }, { name: "Bob", age: 17, email: undefined }, { name: "Charlie", age: 30, email: "charlie@example.com" }, { name: "David", age: 16, email: undefined }]const adultEmails = Iterable.filterMap(users, (user) => user.age >= 18 && user.email ? Result.succeed(user.email) : Result.failVoid)console.log(Array.from(adultEmails)) // ["alice@example.com", "charlie@example.com"]
// Use index in transformationconst items = ["a", "b", "c", "d", "e"]const evenIndexItems = Iterable.filterMap(items, (item, i) => i % 2 === 0 ? Result.succeed(`${i}: ${item}`) : Result.failVoid)console.log(Array.from(evenIndexItems)) // ["0: a", "2: c", "4: e"]Signature
declare const filterMap: { <A, B, X>(f: (input: A, i: number) => Result<B, X>): (self: Iterable<A>) => Iterable<B> <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>): Iterable<B>}Since v2.0.0
filterMapWhile
Section titled “filterMapWhile”Transforms all elements of the Iterable for as long as the specified function succeeds.
Example (Filtering and transforming until failure)
import { Iterable, Result } from "effect"
// Parse numbers until we hit an invalid oneconst strings = ["1", "2", "3", "invalid", "4", "5"]const numbers = Iterable.filterMapWhile(strings, (s) => { const num = parseInt(s) return isNaN(num) ? Result.failVoid : Result.succeed(num)})console.log(Array.from(numbers)) // [1, 2, 3] (stops at "invalid")
// Take elements while they meet a condition and transform themconst values = [2, 4, 6, 7, 8, 10]const doubledEvens = Iterable.filterMapWhile(values, (n) => (n % 2 === 0 ? Result.succeed(n * 2) : Result.failVoid))console.log(Array.from(doubledEvens)) // [4, 8, 12] (stops at 7)
// Process with index until condition failsconst letters = ["a", "b", "c", "d", "e"]const indexedUntilC = Iterable.filterMapWhile(letters, (letter, i) => letter !== "c" ? Result.succeed(`${i}: ${letter}`) : Result.failVoid)console.log(Array.from(indexedUntilC)) // ["0: a", "1: b"] (stops at "c")Signature
declare const filterMapWhile: { <A, B, X>(f: (input: A, i: number) => Result<B, X>): (self: Iterable<A>) => Iterable<B> <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>): Iterable<B>}Since v2.0.0
getFailures
Section titled “getFailures”Returns a lazy iterable containing the failure values from an iterable of
Results, skipping successful results.
Example (Extracting failures)
import { Iterable, Result } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Array.from(Iterable.getFailures([Result.succeed(1), Result.fail("err"), Result.succeed(2)])), [ "err"])Signature
declare const getFailures: <R0, L>(self: Iterable<Result<R0, L>>) => Iterable<L>Since v4.0.0
getSomes
Section titled “getSomes”Retrieves the Some values from an Iterable of Options.
Example (Extracting Some values)
import { Iterable, Option } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Array.from(Iterable.getSomes([Option.some(1), Option.none(), Option.some(2)])), [1, 2])Signature
declare const getSomes: <A>(self: Iterable<Option<A>>) => Iterable<A>Since v2.0.0
getSuccesses
Section titled “getSuccesses”Returns a lazy iterable containing the success values from an iterable of
Results, skipping failed results.
Example (Extracting successes)
import { Iterable, Result } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual( Array.from(Iterable.getSuccesses([Result.succeed(1), Result.fail("err"), Result.succeed(2)])), [1, 2])Signature
declare const getSuccesses: <R0, L>(self: Iterable<Result<R0, L>>) => Iterable<R0>Since v4.0.0
folding
Section titled “folding”countBy
Section titled “countBy”Computes how many elements of the iterable pass the given predicate.
Example (Counting matching elements)
import { Iterable } from "effect"
const result = Iterable.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0)console.log(result) // 2Signature
declare const countBy: { <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => number <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): number}Since v3.16.0
reduce
Section titled “reduce”Reduces an iterable to a single value by applying a function to each element and accumulating the result.
Details
This function applies a reducing function against an accumulator and each element of the iterable (from left to right) to reduce it to a single value.
Example (Reducing an iterable)
import { Iterable } from "effect"
// Sum all numbersconst numbers = [1, 2, 3, 4, 5]const sum = Iterable.reduce(numbers, 0, (acc, n) => acc + n)console.log(sum) // 15
// Find maximum valueconst values = [3, 1, 4, 1, 5, 9, 2]const max = Iterable.reduce(values, -Infinity, Math.max)console.log(max) // 9
// Build an object from key-value pairsconst pairs = [ ["a", 1], ["b", 2], ["c", 3]] as constconst obj = Iterable.reduce(pairs, {} as Record<string, number>, (acc, [key, value]) => { acc[key] = value return acc})console.log(obj) // { a: 1, b: 2, c: 3 }
// Use index in the reducerconst letters = ["a", "b", "c"]const indexed = Iterable.reduce(letters, [] as Array<string>, (acc, letter, i) => { acc.push(`${i}: ${letter}`) return acc})console.log(indexed) // ["0: a", "1: b", "2: c"]Signature
declare const reduce: { <B, A>(b: B, f: (b: B, a: A, i: number) => B): (self: Iterable<A>) => B <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A, i: number) => B): B}Since v2.0.0
Reduces an Iterable from the left, keeping all intermediate results instead of only the final result.
Example (Tracking running results)
import { Iterable } from "effect"
// Running sum of numbersconst numbers = [1, 2, 3, 4, 5]const runningSum = Iterable.scan(numbers, 0, (acc, n) => acc + n)console.log(Array.from(runningSum)) // [0, 1, 3, 6, 10, 15]
// Build strings progressivelyconst letters = ["a", "b", "c"]const progressive = Iterable.scan(letters, "", (acc, letter) => acc + letter)console.log(Array.from(progressive)) // ["", "a", "ab", "abc"]
// Track maximum values seen so farconst values = [3, 1, 4, 1, 5, 9, 2]const runningMax = Iterable.scan(values, -Infinity, Math.max)console.log(Array.from(runningMax)) // [-Infinity, 3, 3, 4, 4, 5, 9, 9]Signature
declare const scan: { <B, A>(b: B, f: (b: B, a: A) => B): (self: Iterable<A>) => Iterable<B> <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A) => B): Iterable<B>}Since v2.0.0
getters
Section titled “getters”Drops a max number of elements from the start of an Iterable
Details
n is normalized to a non-negative integer.
Example (Dropping from the start)
import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]const withoutFirstTwo = Iterable.drop(numbers, 2)console.log(Array.from(withoutFirstTwo)) // [3, 4, 5]
// Dropping more than available returns emptyconst withoutFirstTen = Iterable.drop(numbers, 10)console.log(Array.from(withoutFirstTen)) // []
// Dropping 0 or negative returns all elementsconst all = Iterable.drop(numbers, 0)console.log(Array.from(all)) // [1, 2, 3, 4, 5]
// Combine with take for slicingconst slice = Iterable.take(Iterable.drop(numbers, 1), 3)console.log(Array.from(slice)) // [2, 3, 4]Signature
declare const drop: { (n: number): <A>(self: Iterable<A>) => Iterable<A> <A>(self: Iterable<A>, n: number): Iterable<A>}Since v2.0.0
Gets the first element of a Iterable safely, or None if the Iterable is empty.
Example (Getting the first element)
import { Iterable, Option } from "effect"
const numbers = [1, 2, 3]console.log(Iterable.head(numbers)) // Option.some(1)
const empty = Iterable.empty<number>()console.log(Iterable.head(empty)) // Option.none()
// Safe way to get first elementconst firstEven = Iterable.head(Iterable.filter([1, 3, 4, 5], (x) => x % 2 === 0))console.log(firstEven) // Option.some(4)
// Use with Option methodsconst doubled = Option.map(Iterable.head([5, 10, 15]), (x) => x * 2)console.log(doubled) // Option.some(10)Signature
declare const head: <A>(self: Iterable<A>) => Option<A>Since v2.0.0
headUnsafe
Section titled “headUnsafe”Gets the first element of an Iterable without returning an Option.
When to use
Use when the Iterable is known to be non-empty and direct access to the
first element is preferred over handling Option.none.
Gotchas
Throws if the Iterable is empty.
Example (Getting the first element unsafely)
import { Iterable } from "effect"
const numbers = [1, 2, 3]console.log(Iterable.headUnsafe(numbers)) // 1
const letters = "hello"console.log(Iterable.headUnsafe(letters)) // "h"
// Iterable.headUnsafe(Iterable.empty<number>())// throws Error: "headUnsafe: empty iterable"
// Use only when you're certain the iterable is non-emptyconst nonEmpty = Iterable.range(1, 10)console.log(Iterable.headUnsafe(nonEmpty)) // 1Signature
declare const headUnsafe: <A>(self: Iterable<A>) => ASince v4.0.0
Returns the number of elements in a Iterable.
Example (Counting iterable elements)
import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]console.log(Iterable.size(numbers)) // 5
const empty = Iterable.empty<number>()console.log(Iterable.size(empty)) // 0
// Works with any iterableconst letters = "hello"console.log(Iterable.size(letters)) // 5
// Note: This consumes the entire iterableconst range = Iterable.range(1, 100)console.log(Iterable.size(range)) // 100Signature
declare const size: <A>(self: Iterable<A>) => numberSince v2.0.0
Keeps only a max number of elements from the start of an Iterable, creating a new Iterable.
Details
n is normalized to a non-negative integer.
Example (Taking from the start)
import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]const firstThree = Iterable.take(numbers, 3)console.log(Array.from(firstThree)) // [1, 2, 3]
// Taking more than available returns all elementsconst firstTen = Iterable.take(numbers, 10)console.log(Array.from(firstTen)) // [1, 2, 3, 4, 5]
// Taking 0 or negative returns emptyconst none = Iterable.take(numbers, 0)console.log(Array.from(none)) // []
// Useful with infinite iterablesconst naturals = Iterable.range(1)const firstFive = Iterable.take(naturals, 5)console.log(Array.from(firstFive)) // [1, 2, 3, 4, 5]Signature
declare const take: { (n: number): <A>(self: Iterable<A>) => Iterable<A> <A>(self: Iterable<A>, n: number): Iterable<A>}Since v2.0.0
takeWhile
Section titled “takeWhile”Takes the longest initial Iterable prefix for which all elements satisfy the
specified predicate.
Example (Taking while a predicate holds)
import { Iterable } from "effect"
const numbers = [2, 4, 6, 8, 3, 10, 12]const evenPrefix = Iterable.takeWhile(numbers, (x) => x % 2 === 0)console.log(Array.from(evenPrefix)) // [2, 4, 6, 8]
// With indexconst letters = ["a", "b", "c", "d", "e"]const firstThreeByIndex = Iterable.takeWhile(letters, (_, i) => i < 3)console.log(Array.from(firstThreeByIndex)) // ["a", "b", "c"]
// Stops at first non-matching elementconst mixed = [1, 3, 5, 4, 7, 9]const oddPrefix = Iterable.takeWhile(mixed, (x) => x % 2 === 1)console.log(Array.from(oddPrefix)) // [1, 3, 5]
// Type refinementconst values: Array<string | number> = ["a", "b", "c", 1, "d"]const stringPrefix = Iterable.takeWhile(values, (x): x is string => typeof x === "string")console.log(Array.from(stringPrefix)) // ["a", "b", "c"] (typed as string[])Signature
declare const takeWhile: { <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Iterable<B> <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Iterable<A> <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Iterable<B> <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A>}Since v2.0.0
grouping
Section titled “grouping”Groups equal, consecutive elements of an Iterable into NonEmptyArrays.
Example (Grouping consecutive elements)
import { Iterable } from "effect"
const numbers = [1, 1, 2, 2, 2, 3, 1, 1]const grouped = Iterable.group(numbers)console.log(Array.from(grouped))// [[1, 1], [2, 2, 2], [3], [1, 1]]
const letters = "aabbccaa"const groupedLetters = Iterable.group(letters)console.log(Array.from(groupedLetters))// [["a", "a"], ["b", "b"], ["c", "c"], ["a", "a"]]
// Works with objects using deep equalityconst objects = [ { type: "A", value: 1 }, { type: "A", value: 1 }, { type: "B", value: 2 }, { type: "A", value: 1 }]const groupedObjects = Iterable.group(objects)console.log(Array.from(groupedObjects).length) // 3 groups// Note: Only consecutive equal objects are grouped togetherSignature
declare const group: <A>(self: Iterable<A>) => Iterable<NonEmptyArray<A>>Since v2.0.0
groupBy
Section titled “groupBy”Groups all elements by the string or symbol key returned by f.
Details
Each property in the returned record contains a non-empty array of elements
that produced that key. Unlike group, matching elements do not need to be
consecutive.
Example (Grouping by a key)
import { Iterable } from "effect"
// Group by string lengthconst words = ["a", "bb", "ccc", "dd", "eee", "f"]const byLength = Iterable.groupBy(words, (word) => word.length.toString())console.log(byLength)// { "1": ["a", "f"], "2": ["bb", "dd"], "3": ["ccc", "eee"] }
// Group by first letterconst names = ["Alice", "Bob", "Charlie", "David", "Anna", "Betty"]const byFirstLetter = Iterable.groupBy(names, (name) => name[0])console.log(byFirstLetter)// { "A": ["Alice", "Anna"], "B": ["Bob", "Betty"], "C": ["Charlie"], "D": ["David"] }
// Group by categoryconst items = [ { name: "apple", category: "fruit" }, { name: "carrot", category: "vegetable" }, { name: "banana", category: "fruit" }, { name: "broccoli", category: "vegetable" }]const byCategory = Iterable.groupBy(items, (item) => item.category)console.log(byCategory)// {// "fruit": [{ name: "apple", category: "fruit" }, { name: "banana", category: "fruit" }],// "vegetable": [{ name: "carrot", category: "vegetable" }, { name: "broccoli", category: "vegetable" }]// }
// Group numbers by even/oddconst numbers = [1, 2, 3, 4, 5, 6]const evenOdd = Iterable.groupBy(numbers, (n) => (n % 2 === 0 ? "even" : "odd"))console.log(evenOdd)// { "odd": [1, 3, 5], "even": [2, 4, 6] }Signature
declare const groupBy: { <A, K extends string | symbol>( f: (a: A) => K ): (self: Iterable<A>) => Record<Record.ReadonlyRecord.NonLiteralKey<K>, NonEmptyArray<A>> <A, K extends string | symbol>( self: Iterable<A>, f: (a: A) => K ): Record<Record.ReadonlyRecord.NonLiteralKey<K>, NonEmptyArray<A>>}Since v2.0.0
groupWith
Section titled “groupWith”Groups equal, consecutive elements of an Iterable into NonEmptyArrays using the provided isEquivalent function.
Example (Grouping consecutive elements with custom equivalence)
import { Iterable } from "effect"
// Group consecutive equal numbersconst numbers = [1, 1, 2, 2, 2, 3, 1, 1]const grouped = Iterable.groupWith(numbers, (a, b) => a === b)console.log(Array.from(grouped))// [[1, 1], [2, 2, 2], [3], [1, 1]]
// Case-insensitive grouping of stringsconst words = ["Apple", "APPLE", "banana", "Banana", "cherry"]const caseInsensitive = (a: string, b: string) => a.toLowerCase() === b.toLowerCase()const groupedWords = Iterable.groupWith(words, caseInsensitive)console.log(Array.from(groupedWords))// [["Apple", "APPLE"], ["banana", "Banana"], ["cherry"]]
// Group by approximate equalityconst floats = [1.1, 1.12, 1.9, 2.01, 2.05, 3.5]const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.2const groupedFloats = Iterable.groupWith(floats, approxEqual)console.log(Array.from(groupedFloats))// [[1.1, 1.12], [1.9, 2.01, 2.05], [3.5]]
// Only groups consecutive elementsconst scattered = [1, 2, 1, 2, 1]const scatteredGroups = Iterable.groupWith(scattered, (a, b) => a === b)console.log(Array.from(scatteredGroups))// [[1], [2], [1], [2], [1]] (no grouping since none are consecutive)Signature
declare const groupWith: { <A>(isEquivalent: (self: A, that: A) => boolean): (self: Iterable<A>) => Iterable<NonEmptyArray<A>> <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<NonEmptyArray<A>>}Since v2.0.0
guards
Section titled “guards”isEmpty
Section titled “isEmpty”Checks whether an Iterable is empty.
Example (Checking for emptiness)
import { Iterable } from "effect"import * as assert from "node:assert"
assert.deepStrictEqual(Iterable.isEmpty([]), true)assert.deepStrictEqual(Iterable.isEmpty([1, 2, 3]), false)Signature
declare const isEmpty: <A>(self: Iterable<A>) => self is Iterable<never>Since v2.0.0
mapping
Section titled “mapping”Transforms each element of an iterable using a function.
Details
This is one of the most fundamental operations for working with iterables. It applies a transformation function to each element, creating a new iterable with the transformed values. The operation is lazy, so elements are only transformed when the iterable is consumed.
Example (Mapping elements)
import { Iterable } from "effect"
// Transform numbers to their squaresconst numbers = [1, 2, 3, 4, 5]const squares = Iterable.map(numbers, (x) => x * x)console.log(Array.from(squares)) // [1, 4, 9, 16, 25]
// Use index in transformationconst indexed = Iterable.map(["a", "b", "c"], (char, i) => `${i}: ${char}`)console.log(Array.from(indexed)) // ["0: a", "1: b", "2: c"]
// Chain transformationsconst result = Iterable.map( Iterable.map([1, 2, 3], (x) => x * 2), (x) => x + 1)console.log(Array.from(result)) // [3, 5, 7]Signature
declare const map: { <A, B>(f: (a: NoInfer<A>, i: number) => B): (self: Iterable<A>) => Iterable<B> <A, B>(self: Iterable<A>, f: (a: NoInfer<A>, i: number) => B): Iterable<B>}Since v2.0.0
sequencing
Section titled “sequencing”flatMap
Section titled “flatMap”Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.
Example (Flat mapping iterables)
import { Iterable } from "effect"
// Expand each number to a rangeconst numbers = [1, 2, 3]const expanded = Iterable.flatMap(numbers, (n) => Iterable.range(1, n))console.log(Array.from(expanded)) // [1, 1, 2, 1, 2, 3]
// Split strings into charactersconst words = ["hi", "bye"]const chars = Iterable.flatMap(words, (word) => word)console.log(Array.from(chars)) // ["h", "i", "b", "y", "e"]
// Conditional expansion with empty iterablesconst values = [1, 2, 3, 4, 5]const evenMultiples = Iterable.flatMap(values, (n) => (n % 2 === 0 ? [n, n * 2, n * 3] : []))console.log(Array.from(evenMultiples)) // [2, 4, 6, 4, 8, 12]
// Use index in transformationconst letters = ["a", "b", "c"]const indexed = Iterable.flatMap(letters, (letter, i) => Iterable.replicate(letter, i + 1))console.log(Array.from(indexed)) // ["a", "b", "b", "c", "c", "c"]Signature
declare const flatMap: { <A, B>(f: (a: NoInfer<A>, i: number) => Iterable<B>): (self: Iterable<A>) => Iterable<B> <A, B>(self: Iterable<A>, f: (a: NoInfer<A>, i: number) => Iterable<B>): Iterable<B>}Since v2.0.0
flatMapNullishOr
Section titled “flatMapNullishOr”Transforms elements using a function that may return null or undefined, filtering out the null/undefined results.
When to use
Use when working with APIs or functions that return nullable values, providing a clean way to filter out null or undefined while transforming.
Example (Flat mapping nullable results)
import { Iterable } from "effect"
// Extract valid elements from nullable function resultsconst data = ["1", "2", "invalid", "4"]const parsed = Iterable.flatMapNullishOr(data, (s) => { const num = parseInt(s) return isNaN(num) ? null : num * 2})console.log(Array.from(parsed)) // [2, 4, 8]
// Safe property accessconst objects = [{ nested: { value: 10 } }, { nested: null }, { nested: { value: 20 } }, {}]const values = Iterable.flatMapNullishOr(objects, (obj) => obj.nested?.value)console.log(Array.from(values)) // [10, 20]
// Working with Map.get (returns undefined for missing keys)const map = new Map([ ["a", 1], ["b", 2], ["c", 3]])const keys = ["a", "x", "b", "y", "c"]const foundValues = Iterable.flatMapNullishOr(keys, (key) => map.get(key))console.log(Array.from(foundValues)) // [1, 2, 3]Signature
declare const flatMapNullishOr: { <A, B>(f: (a: A) => B): (self: Iterable<A>) => Iterable<NonNullable<B>> <A, B>(self: Iterable<A>, f: (a: A) => B): Iterable<NonNullable<B>>}Since v4.0.0
flatten
Section titled “flatten”Flattens an Iterable of Iterables into a single Iterable
Example (Flattening nested iterables)
import { Iterable } from "effect"
// Flatten nested arraysconst nested = [ [1, 2], [3, 4], [5, 6]]const flat = Iterable.flatten(nested)console.log(Array.from(flat)) // [1, 2, 3, 4, 5, 6]
// Flatten different iterable typesconst mixed: Array<Iterable<string>> = ["ab", "cd"]const flatMixed = Iterable.flatten(mixed)console.log(Array.from(flatMixed)) // ["a", "b", "c", "d"]
// Flatten deeply nested (only one level)const deepNested = [[[1, 2]], [[3, 4]]]const oneLevelFlat = Iterable.flatten(deepNested)console.log(Array.from(oneLevelFlat).map((arr) => Array.from(arr)))// [[1, 2], [3, 4]] (still contains arrays)
// Empty iterables are handled correctlyconst withEmpty = [[1, 2], [], [3, 4], []]const flatWithEmpty = Iterable.flatten(withEmpty)console.log(Array.from(flatWithEmpty)) // [1, 2, 3, 4]Signature
declare const flatten: <A>(self: Iterable<Iterable<A>>) => Iterable<A>Since v2.0.0
splitting
Section titled “splitting”chunksOf
Section titled “chunksOf”Splits an Iterable into length-n pieces. The last piece will be shorter if n does not evenly divide the length of
the Iterable.
Example (Chunking an iterable)
import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]const chunks = Iterable.chunksOf(numbers, 3)console.log(Array.from(chunks).map((chunk) => Array.from(chunk)))// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Last chunk can be shorterconst uneven = [1, 2, 3, 4, 5, 6, 7]const chunks2 = Iterable.chunksOf(uneven, 3)console.log(Array.from(chunks2).map((chunk) => Array.from(chunk)))// [[1, 2, 3], [4, 5, 6], [7]]
// Chunk size larger than iterableconst small = [1, 2]const chunks3 = Iterable.chunksOf(small, 5)console.log(Array.from(chunks3).map((chunk) => Array.from(chunk)))// [[1, 2]]
// Process data in batchesconst data = Iterable.range(1, 100)const batches = Iterable.chunksOf(data, 10)const batchSums = Iterable.map(batches, (batch) => Iterable.reduce(batch, 0, (sum, n) => sum + n))console.log(Array.from(Iterable.take(batchSums, 3))) // [55, 155, 255]Signature
declare const chunksOf: { (n: number): <A>(self: Iterable<A>) => Iterable<Array<A>> <A>(self: Iterable<A>, n: number): Iterable<Array<A>>}Since v2.0.0
zipping
Section titled “zipping”Takes two Iterables and returns an Iterable of corresponding pairs.
Example (Zipping iterables)
import { Iterable } from "effect"
const numbers = [1, 2, 3]const letters = ["a", "b", "c"]const zipped = Iterable.zip(numbers, letters)console.log(Array.from(zipped)) // [[1, "a"], [2, "b"], [3, "c"]]
// Different lengths - shorter one determines result lengthconst short = [1, 2]const long = ["a", "b", "c", "d"]const partial = Iterable.zip(short, long)console.log(Array.from(partial)) // [[1, "a"], [2, "b"]]
// Works with any iterablesconst range = Iterable.range(1, 3)const word = "abc"const mixed = Iterable.zip(range, word)console.log(Array.from(mixed)) // [[1, "a"], [2, "b"], [3, "c"]]
// Create indexed pairsconst values = ["apple", "banana", "cherry"]const indices = Iterable.range(0, 2)const indexed = Iterable.zip(indices, values)console.log(Array.from(indexed)) // [[0, "apple"], [1, "banana"], [2, "cherry"]]Signature
declare const zip: { <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<[A, B]> <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]>}Since v2.0.0
zipWith
Section titled “zipWith”Applies a function to pairs of elements at the same index in two Iterables, collecting the results. If one
input Iterable is short, excess elements of the longer Iterable are discarded.
Example (Zipping with a combining function)
import { Iterable } from "effect"
// Add corresponding elementsconst a = [1, 2, 3, 4]const b = [10, 20, 30, 40]const sums = Iterable.zipWith(a, b, (x, y) => x + y)console.log(Array.from(sums)) // [11, 22, 33, 44]
// Combine stringsconst firstNames = ["John", "Jane", "Bob"]const lastNames = ["Doe", "Smith", "Johnson"]const fullNames = Iterable.zipWith(firstNames, lastNames, (first, last) => `${first} ${last}`)console.log(Array.from(fullNames)) // ["John Doe", "Jane Smith", "Bob Johnson"]
// Different lengths - stops at shorterconst short = [1, 2]const long = ["a", "b", "c", "d"]const combined = Iterable.zipWith(short, long, (num, letter) => `${num}${letter}`)console.log(Array.from(combined)) // ["1a", "2b"]
// Complex transformationsconst prices = [10.99, 25.5, 5.0]const quantities = [2, 1, 3]const totals = Iterable.zipWith(prices, quantities, (price, qty) => { return Math.round(price * qty * 100) / 100 // round to 2 decimal places})console.log(Array.from(totals)) // [21.98, 25.5, 15]Signature
declare const zipWith: { <B, A, C>(that: Iterable<B>, f: (a: A, b: B) => C): (self: Iterable<A>) => Iterable<C> <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C>}Since v2.0.0