Skip to content

Iterable.ts

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



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 appends
const result = Iterable.append(Iterable.append([1, 2], 3), 4)
console.log(Array.from(result)) // [1, 2, 3, 4]

See

  • prepend for adding one element before the existing elements
  • appendAll for 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>
}

Source

Since v2.0.0

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 types
const 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 needed
const 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

  • append for appending one value instead of another iterable
  • prependAll for yielding another iterable before self

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>
}

Source

Since v2.0.0

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 separator
const numbers = [1, 2, 3, 4]
const withCommas = Iterable.intersperse(numbers, ",")
console.log(Array.from(withCommas)) // [1, ",", 2, ",", 3, ",", 4]
// Join words with spaces
const words = ["hello", "world", "from", "effect"]
const sentence = Iterable.intersperse(words, " ")
console.log(Array.from(sentence).join("")) // "hello world from effect"
// Empty iterable remains empty
const empty = Iterable.empty<string>()
const stillEmpty = Iterable.intersperse(empty, "-")
console.log(Array.from(stillEmpty)) // []
// Single element has no separators added
const single = [42]
const noSeparator = Iterable.intersperse(single, "|")
console.log(Array.from(noSeparator)) // [42]
// Build CSS-like strings
const 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>
}

Source

Since v2.0.0

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 iterable
const 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>
}

Source

Since v2.0.0

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>
}

Source

Since v2.0.0

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 reductions
const hasData = true
const result = hasData ? Iterable.range(1, 5) : Iterable.empty<number>()

Signature

declare const empty: <A = never>() => Iterable<A>

Source

Since v2.0.0

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

  • repeat for repeating an iterable a specific number of times
  • take for bounding the unbounded result before materializing it

Signature

declare const forever: <A>(self: Iterable<A>) => Iterable<A>

Source

Since v4.0.0

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 numbers
const evens = Iterable.makeBy((n) => n * 2, { length: 5 })
console.log(Array.from(evens)) // [0, 2, 4, 6, 8]
// Generate squares
const 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>

Source

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 sequences
const sequences = [Iterable.of("hello"), Iterable.range(1, 3), Iterable.empty<string>()]
// Can be used with flatMap for conditional inclusion
const 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>

Source

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>

Source

Since v2.0.0

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

  • forever for repeating without an upper bound
  • replicate for repeating a single value

Signature

declare const repeat: {
(n: number): <A>(self: Iterable<A>) => Iterable<A>
<A>(self: Iterable<A>, n: number): Iterable<A>
}

Source

Since v4.0.0

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> }

Source

Since v2.0.0

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 sequence
const 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 limit
const 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 countdown
const 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 sequence
const 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>

Source

Since v2.0.0

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]>

Source

Since v2.0.0

Zips this Iterable crosswise with the specified Iterable.

Example (Generating cartesian pairs)

import { Iterable } from "effect"
// All pairs of numbers and letters
const 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 grid
const 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 testing
const 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 product
const 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]>
}

Source

Since v2.0.0

Zips this Iterable crosswise with the specified Iterable using the specified combiner.

Example (Combining cartesian products)

import { Iterable } from "effect"
// Create coordinate pairs
const 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 options
const 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 pairs
const 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 combinations
const 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>
}

Source

Since v2.0.0

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)) // true
console.log(Iterable.contains(numbers, 6)) // false
const letters = "hello"
console.log(Iterable.contains(letters, "l")) // true
console.log(Iterable.contains(letters, "x")) // false
// Works with any iterable
const range = Iterable.range(1, 100)
console.log(Iterable.contains(range, 50)) // true
console.log(Iterable.contains(range, 150)) // false
// Curried version
const containsThree = Iterable.contains(3)
console.log(containsThree([1, 2, 3])) // true
console.log(containsThree([4, 5, 6])) // false

Signature

declare const contains: { <A>(a: A): (self: Iterable<A>) => boolean; <A>(self: Iterable<A>, a: A): boolean }

Source

Since v2.0.0

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 objects
const byId = (a: { id: number }, b: { id: number }) => a.id === b.id
const 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 comparison
const 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 comparison
const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1
const containsApprox = Iterable.containsWith(approxEqual)
const values = [1.0, 2.0, 3.0]
const hasAlmostTwo = containsApprox(values, 2.05)
console.log(hasAlmostTwo) // true

Signature

declare const containsWith: <A>(isEquivalent: (self: A, that: A) => boolean) => {
(a: A): (self: Iterable<A>) => boolean
(self: Iterable<A>, a: A): boolean
}

Source

Since v2.0.0

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 index
const letters = ["a", "b", "c", "d"]
const atEvenIndex = Iterable.findFirst(letters, (_, i) => i % 2 === 0)
console.log(atEvenIndex) // Option.some("a")
// Type refinement
const 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 search
const 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>
}

Source

Since v2.0.0

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 index
const letters = ["a", "b", "c", "d", "e"]
const lastAtEvenIndex = Iterable.findLast(letters, (_, i) => i % 2 === 0)
console.log(lastAtEvenIndex) // Option.some("e") (index 4)
// Type refinement
const 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>
}

Source

Since v2.0.0

Iterates over the Iterable, applying f to each element.

Example (Iterating with side effects)

import { Iterable } from "effect"
// Print each element
const numbers = [1, 2, 3, 4, 5]
Iterable.forEach(numbers, (n) => console.log(n))
// Prints: 1, 2, 3, 4, 5
// Use index in the callback
const letters = ["a", "b", "c"]
Iterable.forEach(letters, (letter, i) => {
console.log(`${i}: ${letter}`)
})
// Prints: "0: a", "1: b", "2: c"
// Side effects with any iterable
const results: Array<number> = []
Iterable.forEach(Iterable.range(1, 5), (n) => {
results.push(n * n)
})
console.log(results) // [1, 4, 9, 16, 25]
// Process in chunks
const 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
}

Source

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 index
const letters = ["a", "b", "c"]
const hasElementAtIndex2 = Iterable.some(letters, (_, i) => i === 2)
console.log(hasElementAtIndex2) // true
// Early termination - stops at first match
const 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 usage
const mixed: Array<string | number> = [1, 2, "hello"]
const hasString = Iterable.some(mixed, (x): x is string => typeof x === "string")
console.log(hasString) // true

Signature

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
}

Source

Since v2.0.0

Deduplicates adjacent elements that are identical.

Example (Deduplicating adjacent elements)

import { Iterable } from "effect"
// Remove adjacent duplicate numbers
const 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 characters
const letters = "aabbccaa"
const dedupedLetters = Iterable.dedupeAdjacent(letters)
console.log(Array.from(dedupedLetters)) // ["a", "b", "c", "a"]
// Works with objects using deep equality
const 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 data
const 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>

Source

Since v2.0.0

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 equality
const 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 deduplication
const 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 property
const 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.id
const dedupedUsers = Iterable.dedupeAdjacentWith(users, byId)
console.log(Array.from(dedupedUsers).map((u) => u.id)) // [1, 2, 3]
// Approximate numeric equality
const floats = [1.0, 1.01, 1.02, 2.0, 2.01, 3.0]
const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1
const 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>
}

Source

Since v2.0.0

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 numbers
const 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 index
const items = ["a", "b", "c", "d"]
const oddPositions = Iterable.filter(items, (_, i) => i % 2 === 1)
console.log(Array.from(oddPositions)) // ["b", "d"]
// Type refinement
const 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 map
const 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>
}

Source

Since v2.0.0

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 ones
const 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 objects
const 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 transformation
const 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>
}

Source

Since v2.0.0

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 one
const 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 them
const 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 fails
const 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>
}

Source

Since v2.0.0

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>

Source

Since v4.0.0

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>

Source

Since v2.0.0

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>

Source

Since v4.0.0

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) // 2

Signature

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
}

Source

Since v3.16.0

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 numbers
const numbers = [1, 2, 3, 4, 5]
const sum = Iterable.reduce(numbers, 0, (acc, n) => acc + n)
console.log(sum) // 15
// Find maximum value
const 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 pairs
const pairs = [
["a", 1],
["b", 2],
["c", 3]
] as const
const 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 reducer
const 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
}

Source

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 numbers
const 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 progressively
const 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 far
const 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>
}

Source

Since v2.0.0

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 empty
const withoutFirstTen = Iterable.drop(numbers, 10)
console.log(Array.from(withoutFirstTen)) // []
// Dropping 0 or negative returns all elements
const all = Iterable.drop(numbers, 0)
console.log(Array.from(all)) // [1, 2, 3, 4, 5]
// Combine with take for slicing
const 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>
}

Source

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 element
const firstEven = Iterable.head(Iterable.filter([1, 3, 4, 5], (x) => x % 2 === 0))
console.log(firstEven) // Option.some(4)
// Use with Option methods
const 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>

Source

Since v2.0.0

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-empty
const nonEmpty = Iterable.range(1, 10)
console.log(Iterable.headUnsafe(nonEmpty)) // 1

Signature

declare const headUnsafe: <A>(self: Iterable<A>) => A

Source

Since 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 iterable
const letters = "hello"
console.log(Iterable.size(letters)) // 5
// Note: This consumes the entire iterable
const range = Iterable.range(1, 100)
console.log(Iterable.size(range)) // 100

Signature

declare const size: <A>(self: Iterable<A>) => number

Source

Since 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 elements
const firstTen = Iterable.take(numbers, 10)
console.log(Array.from(firstTen)) // [1, 2, 3, 4, 5]
// Taking 0 or negative returns empty
const none = Iterable.take(numbers, 0)
console.log(Array.from(none)) // []
// Useful with infinite iterables
const 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>
}

Source

Since v2.0.0

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 index
const 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 element
const 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 refinement
const 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>
}

Source

Since v2.0.0

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 equality
const 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 together

Signature

declare const group: <A>(self: Iterable<A>) => Iterable<NonEmptyArray<A>>

Source

Since v2.0.0

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 length
const 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 letter
const 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 category
const 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/odd
const 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>>
}

Source

Since v2.0.0

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 numbers
const 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 strings
const 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 equality
const floats = [1.1, 1.12, 1.9, 2.01, 2.05, 3.5]
const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.2
const 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 elements
const 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>>
}

Source

Since v2.0.0

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>

Source

Since v2.0.0

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 squares
const 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 transformation
const indexed = Iterable.map(["a", "b", "c"], (char, i) => `${i}: ${char}`)
console.log(Array.from(indexed)) // ["0: a", "1: b", "2: c"]
// Chain transformations
const 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>
}

Source

Since v2.0.0

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 range
const 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 characters
const words = ["hi", "bye"]
const chars = Iterable.flatMap(words, (word) => word)
console.log(Array.from(chars)) // ["h", "i", "b", "y", "e"]
// Conditional expansion with empty iterables
const 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 transformation
const 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>
}

Source

Since v2.0.0

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 results
const 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 access
const 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>>
}

Source

Since v4.0.0

Flattens an Iterable of Iterables into a single Iterable

Example (Flattening nested iterables)

import { Iterable } from "effect"
// Flatten nested arrays
const 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 types
const 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 correctly
const 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>

Source

Since v2.0.0

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 shorter
const 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 iterable
const small = [1, 2]
const chunks3 = Iterable.chunksOf(small, 5)
console.log(Array.from(chunks3).map((chunk) => Array.from(chunk)))
// [[1, 2]]
// Process data in batches
const 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>>
}

Source

Since v2.0.0

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 length
const 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 iterables
const 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 pairs
const 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]>
}

Source

Since v2.0.0

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 elements
const 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 strings
const 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 shorter
const 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 transformations
const 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>
}

Source

Since v2.0.0