Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Iterable

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.

50 exports Since v2.0.0 Source

Combining

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.

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<B | A>;
<A, B>(self: Iterable<A>, last: B): Iterable<A | B>;
}

Example Appending an element

import { Iterable } from "effect"
const numbers = [1, 2, 3]
Array.from(Iterable.append(numbers, 4)) // => [1, 2, 3, 4]

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.

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<B | A>;
<A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>;
}

Example Concatenating iterables

import { Iterable } from "effect"
Array.from(Iterable.appendAll([1, 2, 3], [4, 5, 6])) // => [1, 2, 3, 4, 5, 6]
// Works with different iterable types
const numbers = [1, 2]
const letters = "abc"
const mixed = Iterable.appendAll(numbers, letters)
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]
Array.from(Iterable.take(Iterable.appendAll(finite, infinite), 5)) // => [0, -1, -2, 1, 2]

Zips this Iterable crosswise with the specified Iterable.

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

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)
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)
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)
Array.from(testMatrix, ([browser, device]) => `${browser}:${device}`) // => ["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)
Array.from(withEmpty) // => []

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

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

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})`)
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 })
)
Array.from(products, ({ color, size }) => `${size}:${color}`) // => ["S:red", "S:blue", "M:red", "M:blue", "L:red", "L: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)
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}`
)
Array.from(testCases) // => ["admin_can_read", "admin_can_write", "admin_can_delete", "user_can_read", "user_can_write", "user_can_delete"]

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.

Signature

declare const intersperse: {
<B>(middle: B): <A>(self: Iterable<A>) => Iterable<B | A>;
<A, B>(self: Iterable<A>, middle: B): Iterable<A | B>;
}

Example Interspersing separators

import { Iterable } from "effect"
// Join numbers with separator
const numbers = [1, 2, 3, 4]
const withCommas = Iterable.intersperse(numbers, ",")
Array.from(withCommas) // => [1, ",", 2, ",", 3, ",", 4]
// Join words with spaces
const words = ["hello", "world", "from", "effect"]
const sentence = Iterable.intersperse(words, " ")
Array.from(sentence).join("") // => "hello world from effect"
// Empty iterable remains empty
const empty = Iterable.empty<string>()
const stillEmpty = Iterable.intersperse(empty, "-")
Array.from(stillEmpty) // => []
// Single element has no separators added
const single = [42]
const noSeparator = Iterable.intersperse(single, "|")
Array.from(noSeparator) // => [42]
// Build CSS-like strings
const styles = ["color: red", "font-size: 14px", "margin: 10px"]
const css = Iterable.intersperse(styles, "; ")
Array.from(css).join("") // => "color: red; font-size: 14px; margin: 10px"
Since v2.0.0

Prepends an element to the front of an Iterable, creating a new Iterable.

Signature

declare const prepend: {
<B>(head: B): <A>(self: Iterable<A>) => Iterable<B | A>;
<A, B>(self: Iterable<A>, head: B): Iterable<A | B>;
}

Example Prepending an element

import { Iterable } from "effect"
const numbers = [2, 3, 4]
const withOne = Iterable.prepend(numbers, 1)
Array.from(withOne) // => [1, 2, 3, 4]
// Works with any iterable
const letters = "abc"
const withZ = Iterable.prepend(letters, "z")
Array.from(withZ) // => ["z", "a", "b", "c"]

Prepends the specified prefix iterable to the beginning of the specified iterable.

Signature

declare const prependAll: {
<B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<B | A>;
<A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>;
}

Example Prepending another iterable

import { Iterable } from "effect"
Array.from(Iterable.prependAll([1, 2], ["a", "b"])) // => ["a", "b", 1, 2]

Constructors

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.

Signature

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

Example Creating an empty iterable

import { Iterable } from "effect"
Array.from(Iterable.empty<string>()) // => []
Since v4.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 function forever<A>(self: Iterable<A>): Iterable<A>
Since v2.0.0

Creates an iterable by applying a function to consecutive integers.

Details

The function is called with each index starting from 0. If a length is provided, it is rounded down and normalized to at least 1, with NaN treated as 1. If no length is specified, the iterable is infinite.

Signature

declare function makeBy<A>(f: (i: number) => A, options?: {
readonly length?: number;
}): Iterable<A>

Example Generating values by index

import { Iterable } from "effect"
// Generate first 5 even numbers
const evens = Iterable.makeBy((n) => n * 2, { length: 5 })
Array.from(evens) // => [0, 2, 4, 6, 8]
// Generate squares
const squares = Iterable.makeBy((n) => n * n, { length: 4 })
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)
Array.from(first10) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
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.

Signature

declare function of<A>(a: A): Iterable<A>

Example Wrapping a single value

import { Iterable } from "effect"
const single = Iterable.of(42)
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()
)
Array.from(evensOnly) // => [2, 4]
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.

Signature

declare function range(start: number, end?: number): Iterable<number>

Example Creating a range

import { Iterable } from "effect"
Array.from(Iterable.range(1, 3)) // => [1, 2, 3]
Since v4.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. n is rounded down and normalized to at least 1, with NaN treated as 1. 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>;
}

Returns a Iterable containing a value repeated the specified number of times.

Details

n is rounded down and normalized to an integer greater than or equal to 1. NaN is treated as 1.

Signature

declare const replicate: {
(n: number): <A>(a: A) => Iterable<A>;
<A>(a: A, n: number): Iterable<A>;
}

Example Repeating a value

import { Iterable } from "effect"
Array.from(Iterable.replicate("a", 3)) // => ["a", "a", "a"]
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.

Signature

declare function unfold<B, A>(b: B, f: (b: B) => Option<readonly [A, B]>): Iterable<A>

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)
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())
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())
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])
})
Array.from(collatz) // => [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2]

Converting

Takes a record and returns an Iterable of tuples containing its keys and values.

Signature

declare function fromRecord<K extends string, A>(self: Readonly<Record<K, A>>): Iterable<[K, A]>

Example Converting a record to entries

import { Iterable } from "effect"
const x = { a: 1, b: 2, c: 3 }
Array.from(Iterable.fromRecord(x)) // => [["a", 1], ["b", 2], ["c", 3]]

Filtering

Deduplicates adjacent elements that are identical.

Signature

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

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)
Array.from(deduped) // => [1, 2, 3, 1]
// Remove adjacent duplicate characters
const letters = "aabbccaa"
const dedupedLetters = Iterable.dedupeAdjacent(letters)
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)
Array.from(dedupedObjects, (object) => object.type) // => ["A", "B", "A"]
// Clean up streaming data
const sensorData = [100, 100, 100, 101, 101, 102, 102, 102, 100]
const cleanedData = Iterable.dedupeAdjacent(sensorData)
Array.from(cleanedData) // => [100, 101, 102, 100]

Deduplicates adjacent elements that are identical using the provided isEquivalent function.

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

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)
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)
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)
Array.from(dedupedUsers, (user) => user.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)
Array.from(dedupedFloats) // => [1, 2, 3]
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.

Signature

declare const filter: {
<A, B>(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>(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>;
}

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)
Array.from(evens) // => [2, 4, 6]
// Filter with index
const items = ["a", "b", "c", "d"]
const oddPositions = Iterable.filter(items, (_, i) => i % 2 === 1)
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"
)
Array.from(onlyStrings) // => ["hello", "world"]
// Combine with map
const processed = Iterable.map(
Iterable.filter([1, 2, 3, 4, 5], (x) => x > 2),
(x) => x * 10
)
Array.from(processed) // => [30, 40, 50]

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.

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

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)
})
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
)
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
)
Array.from(evenIndexItems) // => ["0: a", "2: c", "4: e"]

Transforms all elements of the Iterable for as long as the specified function succeeds.

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

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)
})
Array.from(numbers) // => [1, 2, 3]
// 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
)
Array.from(doubledEvens) // => [4, 8, 12]
// 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
)
Array.from(indexedUntilC) // => ["0: a", "1: b"]

Returns a lazy iterable containing the failure values from an iterable of Results, skipping successful results.

Signature

declare function getFailures<R0, L>(self: Iterable<Result<R0, L>>): Iterable<L>

Example Extracting failures

import { Iterable, Result } from "effect"
Array.from(Iterable.getFailures([
Result.succeed(1),
Result.fail("err"),
Result.succeed(2)
])) // => ["err"]
Since v2.0.0

Retrieves the Some values from an Iterable of Options.

Signature

declare function getSomes<A>(self: Iterable<Option<A>>): Iterable<A>

Example Extracting Some values

import { Iterable, Option } from "effect"
Array.from(Iterable.getSomes([Option.some(1), Option.none(), Option.some(2)])) // => [1, 2]

Returns a lazy iterable containing the success values from an iterable of Results, skipping failed results.

Signature

declare function getSuccesses<R0, L>(self: Iterable<Result<R0, L>>): Iterable<R0>

Example Extracting successes

import { Iterable, Result } from "effect"
Array.from(Iterable.getSuccesses([
Result.succeed(1),
Result.fail("err"),
Result.succeed(2)
])) // => [1, 2]

Folding

Since v3.16.0

Computes how many elements of the iterable pass the given predicate.

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

Example Counting matching elements

import { Iterable } from "effect"
Iterable.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0) // => 2
Since v2.0.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.

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

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)
sum // => 15
// Find maximum value
const values = [3, 1, 4, 1, 5, 9, 2]
Iterable.reduce(values, -Infinity, (max, value) => Math.max(max, value)) // => 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
}
)
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
}
)
indexed // => ["0: a", "1: b", "2: c"]
Since v2.0.0

Reduces an Iterable from the left, keeping all intermediate results instead of only the final result.

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

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)
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)
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)
Array.from(runningMax) // => [-Infinity, 3, 3, 4, 4, 5, 9, 9]

Getters

Since v2.0.0

Drops a max number of elements from the start of an Iterable

Details

n is rounded down and normalized to a non-negative integer. NaN is treated as 0.

Signature

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

Example Dropping from the start

import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]
const withoutFirstTwo = Iterable.drop(numbers, 2)
Array.from(withoutFirstTwo) // => [3, 4, 5]
// Dropping more than available returns empty
const withoutFirstTen = Iterable.drop(numbers, 10)
Array.from(withoutFirstTen) // => []
// Dropping 0 or negative returns all elements
const all = Iterable.drop(numbers, 0)
Array.from(all) // => [1, 2, 3, 4, 5]
// Combine with take for slicing
const slice = Iterable.take(Iterable.drop(numbers, 1), 3)
Array.from(slice) // => [2, 3, 4]

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.

Signature

declare function headUnsafe<A>(self: Iterable<A>): A

Example Getting the first element unsafely

import { Iterable } from "effect"
const numbers = [1, 2, 3]
Iterable.headUnsafe(numbers) // => 1
const letters = "hello"
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)
Iterable.headUnsafe(nonEmpty) // => 1
Since v2.0.0

Returns the number of elements in a Iterable.

Signature

declare function size<A>(self: Iterable<A>): number

Example Counting iterable elements

import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]
Iterable.size(numbers) // => 5
const empty = Iterable.empty<number>()
Iterable.size(empty) // => 0
// Works with any iterable
const letters = "hello"
Iterable.size(letters) // => 5
// Note: This consumes the entire iterable
const range = Iterable.range(1, 100)
Iterable.size(range) // => 100
Since v2.0.0

Keeps only a max number of elements from the start of an Iterable, creating a new Iterable.

Details

n is rounded down and normalized to a non-negative integer. NaN is treated as 0.

Signature

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

Example Taking from the start

import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]
const firstThree = Iterable.take(numbers, 3)
Array.from(firstThree) // => [1, 2, 3]
// Taking more than available returns all elements
const firstTen = Iterable.take(numbers, 10)
Array.from(firstTen) // => [1, 2, 3, 4, 5]
// Taking 0 or negative returns empty
const none = Iterable.take(numbers, 0)
Array.from(none) // => []
// Useful with infinite iterables
const naturals = Iterable.range(1)
const firstFive = Iterable.take(naturals, 5)
Array.from(firstFive) // => [1, 2, 3, 4, 5]

Takes the longest initial Iterable prefix for which all elements satisfy the specified predicate.

Signature

declare const takeWhile: {
<A, B>(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>(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>;
}

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)
Array.from(evenPrefix) // => [2, 4, 6, 8]
// With index
const letters = ["a", "b", "c", "d", "e"]
const firstThreeByIndex = Iterable.takeWhile(letters, (_, i) => i < 3)
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)
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"
)
Array.from(stringPrefix) // => ["a", "b", "c"]

Grouping

Since v2.0.0

Groups equal, consecutive elements of an Iterable into NonEmptyArrays.

Signature

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

Example Grouping consecutive elements

import { Iterable } from "effect"
const numbers = [1, 1, 2, 2, 2, 3, 1, 1]
const grouped = Iterable.group(numbers)
Array.from(grouped) // => [[1, 1], [2, 2, 2], [3], [1, 1]]
const letters = "aabbccaa"
const groupedLetters = Iterable.group(letters)
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)
Array.from(groupedObjects).length // => 3
// Note: Only consecutive equal objects are grouped together
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.

Gotchas

When the key function returns a finite union of string literals or unique symbols, the result preserves those keys as optional properties because the input may not produce every key. Open string and symbol key types retain their record index signatures.

Signature

declare const groupBy: {
<A, K extends string | symbol>(f: (a: A) => K): (self: Iterable<A>) => GroupByResult<K, [A, ...Array<A>]>;
<A, K extends string | symbol>(self: Iterable<A>, f: (a: A) => K): GroupByResult<K, [A, ...Array<A>]>;
}

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())
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])
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)
Object.keys(byCategory) // => ["fruit", "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")
evenOdd // => { odd: [1, 3, 5], even: [2, 4, 6] }

Groups equal, consecutive elements of an Iterable into NonEmptyArrays using the provided isEquivalent function.

Signature

declare const groupWith: {
<A>(isEquivalent: (self: A, that: A) => boolean): (self: Iterable<A>) => Iterable<[A, ...Array<A>]>;
<A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<[A, ...Array<A>]>;
}

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)
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)
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)
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)
Array.from(scatteredGroups) // => [[1], [2], [1], [2], [1]]

Guards

Since v2.0.0

Checks whether an Iterable is empty.

Signature

declare function isEmpty<A>(self: Iterable<A>): self is Iterable<never, any, any>

Example Checking for emptiness

import { Iterable } from "effect"
Iterable.isEmpty([]) // => true
Iterable.isEmpty([1, 2, 3]) // => false

Mapping

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.

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

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)
Array.from(squares) // => [1, 4, 9, 16, 25]
// Use index in transformation
const indexed = Iterable.map(["a", "b", "c"], (char, i) => `${i}: ${char}`)
Array.from(indexed) // => ["0: a", "1: b", "2: c"]
Array.from(Iterable.map(
Iterable.map([1, 2, 3], (x) => x * 2),
(x) => x + 1
)) // => [3, 5, 7]

Predicates

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).

Signature

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

Example Checking membership

import { Iterable } from "effect"
const numbers = [1, 2, 3, 4, 5]
Iterable.contains(numbers, 3) // => true
Iterable.contains(numbers, 6) // => false
const letters = "hello"
Iterable.contains(letters, "l") // => true
Iterable.contains(letters, "x") // => false
// Works with any iterable
const range = Iterable.range(1, 100)
Iterable.contains(range, 50) // => true
Iterable.contains(range, 150) // => false
// Curried version
const containsThree = Iterable.contains(3)
containsThree([1, 2, 3]) // => true
containsThree([4, 5, 6]) // => false

Returns a function that checks if an Iterable contains a given value using a provided isEquivalent function.

Signature

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

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 })
hasUser1 // => true
// 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")
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)
hasAlmostTwo // => true
Since v2.0.0

Checks whether a predicate holds true for some Iterable element.

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

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)
hasEven // => true
const allOdd = [1, 3, 5, 7]
const hasEvenInAllOdd = Iterable.some(allOdd, (x) => x % 2 === 0)
hasEvenInAllOdd // => false
// With index
const letters = ["a", "b", "c"]
const hasElementAtIndex2 = Iterable.some(letters, (_, i) => i === 2)
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
)
hasEvenInInfiniteOdds // => false
// Type guard usage
const mixed: Array<string | number> = [1, 2, "hello"]
const hasString = Iterable.some(
mixed,
(x): x is string => typeof x === "string"
)
hasString // => true

Searching

Returns the first element that satisfies the specified predicate, or None if no such element exists.

Signature

declare const findFirst: {
<A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B>;
<A, B>(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>(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>;
}

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)
firstEven // => Option.some(4)
const firstGreaterThan10 = Iterable.findFirst(numbers, (x) => x > 10)
firstGreaterThan10 // => Option.none()
// With index
const letters = ["a", "b", "c", "d"]
const atEvenIndex = Iterable.findFirst(letters, (_, i) => i % 2 === 0)
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"
)
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()
})
findSquareRoot // => Option.some(1)
Since v2.0.0

Finds the last element for which a predicate holds.

Signature

declare const findLast: {
<A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B>;
<A, B>(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>(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>;
}

Example Finding the last match

import { Iterable, Option } from "effect"
const numbers = [1, 3, 4, 6, 8, 2]
const lastEven = Iterable.findLast(numbers, (x) => x % 2 === 0)
lastEven // => Option.some(2)
const lastGreaterThan10 = Iterable.findLast(numbers, (x) => x > 10)
lastGreaterThan10 // => Option.none()
// With index
const letters = ["a", "b", "c", "d", "e"]
const lastAtEvenIndex = Iterable.findLast(letters, (_, i) => i % 2 === 0)
lastAtEvenIndex // => Option.some("e")
// Type refinement
const mixed: Array<string | number> = [1, "hello", 2, "world", 3]
const lastString = Iterable.findLast(
mixed,
(x): x is string => typeof x === "string"
)
lastString // => Option.some("world")

Sequencing

Since v2.0.0

Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.

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

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))
Array.from(expanded) // => [1, 1, 2, 1, 2, 3]
// Split strings into characters
const words = ["hi", "bye"]
const chars = Iterable.flatMap(words, (word) => word)
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] : []
)
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)
)
Array.from(indexed) // => ["a", "b", "b", "c", "c", "c"]

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.

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

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
})
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)
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))
Array.from(foundValues) // => [1, 2, 3]
Since v2.0.0

Flattens an Iterable of Iterables into a single Iterable

Signature

declare function flatten<A>(self: Iterable<Iterable<A, any, any>>): Iterable<A>

Example Flattening nested iterables

import { Iterable } from "effect"
// Flatten nested arrays
const nested = [[1, 2], [3, 4], [5, 6]]
const flat = Iterable.flatten(nested)
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)
Array.from(flatMixed) // => ["a", "b", "c", "d"]
// Flatten deeply nested (only one level)
const deepNested = [[[1, 2]], [[3, 4]]]
const oneLevelFlat = Iterable.flatten(deepNested)
Array.from(oneLevelFlat) // => [[1, 2], [3, 4]]
// [[1, 2], [3, 4]] (still contains arrays)
// Empty iterables are handled correctly
const withEmpty = [[1, 2], [], [3, 4], []]
const flatWithEmpty = Iterable.flatten(withEmpty)
Array.from(flatWithEmpty) // => [1, 2, 3, 4]

Splitting

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. n is rounded down and normalized to at least 1; NaN and non-positive values therefore produce singleton pieces.

Signature

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

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)
Array.from(chunks) // => [[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)
Array.from(chunks2) // => [[1, 2, 3], [4, 5, 6], [7]]
// Chunk size larger than iterable
const small = [1, 2]
const chunks3 = Iterable.chunksOf(small, 5)
Array.from(chunks3) // => [[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)
)
Array.from(Iterable.take(batchSums, 3)) // => [55, 155, 255]

Traversing

Since v2.0.0

Iterates over the Iterable, applying f to each element.

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

Example Iterating with side effects

import { Iterable } from "effect"
// Collect each visited element
const numbers = [1, 2, 3, 4, 5]
const visited: Array<number> = []
Iterable.forEach(numbers, (n) => visited.push(n))
visited // => [1, 2, 3, 4, 5]
// Use index in the callback
const letters = ["a", "b", "c"]
const indexed: Array<string> = []
Iterable.forEach(letters, (letter, i) => {
indexed.push(`${i}: ${letter}`)
})
indexed // => ["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)
})
results // => [1, 4, 9, 16, 25]
// Process in chunks
const data = Iterable.chunksOf([1, 2, 3, 4, 5, 6], 2)
const processed: Array<Array<number>> = []
Iterable.forEach(data, (chunk) => {
processed.push(Array.from(chunk))
})
processed // => [[1, 2], [3, 4], [5, 6]]

Zipping

Since v2.0.0

Takes two Iterables and returns an Iterable of corresponding pairs.

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

Example Zipping iterables

import { Iterable } from "effect"
const numbers = [1, 2, 3]
const letters = ["a", "b", "c"]
const zipped = Iterable.zip(numbers, letters)
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)
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)
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)
Array.from(indexed) // => [[0, "apple"], [1, "banana"], [2, "cherry"]]
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.

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

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)
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}`
)
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}`
)
Array.from(combined) // => ["1a", "2b"]
// Complex transformations
const prices = [10.99, 25.50, 5.00]
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
})
Array.from(totals) // => [21.98, 25.5, 15]