/**
* This module provides utility functions for working with Iterables in TypeScript.
*
* @since 2.0.0
*/
import type { NonEmptyArray } from "./Array.js"
import type { Either } from "./Either.js"
import * as E from "./Either.js"
import * as Equal from "./Equal.js"
import { dual, identity } from "./Function.js"
import type { Option } from "./Option.js"
import * as O from "./Option.js"
import { isBoolean } from "./Predicate.js"
import type * as Record from "./Record.js"
import * as Tuple from "./Tuple.js"
import type { NoInfer } from "./Types.js"
/**
* Return a `Iterable` with element `i` initialized with `f(i)`.
*
* If the `length` is not specified, the `Iterable` will be infinite.
*
* **Note**. `length` is normalized to an integer >= 1.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { makeBy } from "effect/Iterable"
*
* assert.deepStrictEqual(Array.from(makeBy(n => n * 2, { length: 5 })), [0, 2, 4, 6, 8])
* ```
*
* @category constructors
* @since 2.0.0
*/
export const makeBy = (f: (i: number) => A, options?: {
readonly length?: number
}): Iterable => {
const max = options?.length !== undefined ? Math.max(1, Math.floor(options.length)) : Infinity
return {
[Symbol.iterator]() {
let i = 0
return {
next(): IteratorResult {
if (i < max) {
return { value: f(i++), done: false }
}
return { done: true, value: undefined }
}
}
}
}
}
/**
* Return a `Iterable` containing a range of integers, including both endpoints.
*
* If `end` is omitted, the range will not have an upper bound.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { range } from "effect/Iterable"
*
* assert.deepStrictEqual(Array.from(range(1, 3)), [1, 2, 3])
* ```
*
* @category constructors
* @since 2.0.0
*/
export const range = (start: number, end?: number): Iterable => {
if (end === undefined) {
return makeBy((i) => start + i)
}
return makeBy((i) => start + i, {
length: start <= end ? end - start + 1 : 1
})
}
/**
* Return a `Iterable` containing a value repeated the specified number of times.
*
* **Note**. `n` is normalized to an integer >= 1.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { replicate } from "effect/Iterable"
*
* assert.deepStrictEqual(Array.from(replicate("a", 3)), ["a", "a", "a"])
* ```
*
* @category constructors
* @since 2.0.0
*/
export const replicate: {
/**
* Return a `Iterable` containing a value repeated the specified number of times.
*
* **Note**. `n` is normalized to an integer >= 1.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { replicate } from "effect/Iterable"
*
* assert.deepStrictEqual(Array.from(replicate("a", 3)), ["a", "a", "a"])
* ```
*
* @category constructors
* @since 2.0.0
*/
(n: number): (a: A) => Iterable
/**
* Return a `Iterable` containing a value repeated the specified number of times.
*
* **Note**. `n` is normalized to an integer >= 1.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { replicate } from "effect/Iterable"
*
* assert.deepStrictEqual(Array.from(replicate("a", 3)), ["a", "a", "a"])
* ```
*
* @category constructors
* @since 2.0.0
*/
(a: A, n: number): Iterable
} = dual(2, (a: A, n: number): Iterable => makeBy(() => a, { length: n }))
/**
* Takes a record and returns an Iterable of tuples containing its keys and values.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { fromRecord } from "effect/Iterable"
*
* const x = { a: 1, b: 2, c: 3 }
* assert.deepStrictEqual(Array.from(fromRecord(x)), [["a", 1], ["b", 2], ["c", 3]])
* ```
*
* @category conversions
* @since 2.0.0
*/
export const fromRecord = (self: Readonly>): Iterable<[K, A]> => ({
*[Symbol.iterator]() {
for (const key in self) {
if (Object.prototype.hasOwnProperty.call(self, key)) {
yield [key, self[key]]
}
}
}
})
/**
* Prepend an element to the front of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
export const prepend: {
/**
* Prepend an element to the front of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
(head: B): (self: Iterable) => Iterable
/**
* Prepend an element to the front of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
(self: Iterable, head: B): Iterable
} = dual(2, (self: Iterable, head: B): Iterable => prependAll(self, [head]))
/**
* Prepends the specified prefix iterable to the beginning of the specified iterable.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Iterable } from "effect"
*
* assert.deepStrictEqual(
* Array.from(Iterable.prependAll([1, 2], ["a", "b"])),
* ["a", "b", 1, 2]
* )
* ```
*
* @category concatenating
* @since 2.0.0
*/
export const prependAll: {
/**
* Prepends the specified prefix iterable to the beginning of the specified iterable.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Iterable } from "effect"
*
* assert.deepStrictEqual(
* Array.from(Iterable.prependAll([1, 2], ["a", "b"])),
* ["a", "b", 1, 2]
* )
* ```
*
* @category concatenating
* @since 2.0.0
*/
(that: Iterable): (self: Iterable) => Iterable
/**
* Prepends the specified prefix iterable to the beginning of the specified iterable.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Iterable } from "effect"
*
* assert.deepStrictEqual(
* Array.from(Iterable.prependAll([1, 2], ["a", "b"])),
* ["a", "b", 1, 2]
* )
* ```
*
* @category concatenating
* @since 2.0.0
*/
(self: Iterable, that: Iterable): Iterable
} = dual(
2,
(self: Iterable, that: Iterable): Iterable => appendAll(that, self)
)
/**
* Append an element to the end of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
export const append: {
/**
* Append an element to the end of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
(last: B): (self: Iterable) => Iterable
/**
* Append an element to the end of an `Iterable`, creating a new `Iterable`.
*
* @category concatenating
* @since 2.0.0
*/
(self: Iterable, last: B): Iterable
} = dual(2, (self: Iterable, last: B): Iterable => appendAll(self, [last]))
/**
* Concatenates two iterables, combining their elements.
*
* @category concatenating
* @since 2.0.0
*/
export const appendAll: {
/**
* Concatenates two iterables, combining their elements.
*
* @category concatenating
* @since 2.0.0
*/
(that: Iterable): (self: Iterable) => Iterable
/**
* Concatenates two iterables, combining their elements.
*
* @category concatenating
* @since 2.0.0
*/
(self: Iterable, that: Iterable): Iterable
} = dual(
2,
(self: Iterable, that: Iterable): Iterable => ({
[Symbol.iterator]() {
const iterA = self[Symbol.iterator]()
let doneA = false
let iterB: Iterator
return {
next() {
if (!doneA) {
const r = iterA.next()
if (r.done) {
doneA = true
iterB = that[Symbol.iterator]()
return iterB.next()
}
return r
}
return iterB.next()
}
}
}
})
)
/**
* Reduce an `Iterable` from the left, keeping all intermediate results instead of only the final result.
*
* @category folding
* @since 2.0.0
*/
export const scan: {
/**
* Reduce an `Iterable` from the left, keeping all intermediate results instead of only the final result.
*
* @category folding
* @since 2.0.0
*/
(b: B, f: (b: B, a: A) => B): (self: Iterable) => Iterable
/**
* Reduce an `Iterable` from the left, keeping all intermediate results instead of only the final result.
*
* @category folding
* @since 2.0.0
*/
(self: Iterable, b: B, f: (b: B, a: A) => B): Iterable
} = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): Iterable => ({
[Symbol.iterator]() {
let acc = b
let iterator: Iterator | undefined
function next() {
if (iterator === undefined) {
iterator = self[Symbol.iterator]()
return { done: false, value: acc }
}
const result = iterator.next()
if (result.done) {
return result
}
acc = f(acc, result.value)
return { done: false, value: acc }
}
return { next }
}
}))
/**
* Determine if an `Iterable` is empty
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { isEmpty } from "effect/Iterable"
*
* assert.deepStrictEqual(isEmpty([]), true);
* assert.deepStrictEqual(isEmpty([1, 2, 3]), false);
* ```
*
* @category guards
* @since 2.0.0
*/
export const isEmpty = (self: Iterable): self is Iterable => {
const iterator = self[Symbol.iterator]()
return iterator.next().done === true
}
/**
* Return the number of elements in a `Iterable`.
*
* @category getters
* @since 2.0.0
*/
export const size = (self: Iterable): number => {
const iterator = self[Symbol.iterator]()
let count = 0
while (!iterator.next().done) {
count++
}
return count
}
/**
* Get the first element of a `Iterable`, or `None` if the `Iterable` is empty.
*
* @category getters
* @since 2.0.0
*/
export const head = (self: Iterable): Option => {
const iterator = self[Symbol.iterator]()
const result = iterator.next()
return result.done ? O.none() : O.some(result.value)
}
/**
* Get the first element of a `Iterable`, or throw an error if the `Iterable` is empty.
*
* @category getters
* @since 3.3.0
*/
export const unsafeHead = (self: Iterable): A => {
const iterator = self[Symbol.iterator]()
const result = iterator.next()
if (result.done) throw new Error("unsafeHead: empty iterable")
return result.value
}
/**
* Keep only a max number of elements from the start of an `Iterable`, creating a new `Iterable`.
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
export const take: {
/**
* Keep only a max number of elements from the start of an `Iterable`, creating a new `Iterable`.
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
(n: number): (self: Iterable) => Iterable
/**
* Keep only a max number of elements from the start of an `Iterable`, creating a new `Iterable`.
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
(self: Iterable, n: number): Iterable
} = dual(2, (self: Iterable, n: number): Iterable => ({
[Symbol.iterator]() {
let i = 0
const iterator = self[Symbol.iterator]()
return {
next() {
if (i < n) {
i++
return iterator.next()
}
return { done: true, value: undefined }
}
}
}
}))
/**
* Calculate the longest initial Iterable for which all element satisfy the specified predicate, creating a new `Iterable`.
*
* @category getters
* @since 2.0.0
*/
export const takeWhile: {
/**
* Calculate the longest initial Iterable for which all element satisfy the specified predicate, creating a new `Iterable`.
*
* @category getters
* @since 2.0.0
*/
(refinement: (a: NoInfer, i: number) => a is B): (self: Iterable) => Iterable
/**
* Calculate the longest initial Iterable for which all element satisfy the specified predicate, creating a new `Iterable`.
*
* @category getters
* @since 2.0.0
*/
(predicate: (a: NoInfer, i: number) => boolean): (self: Iterable) => Iterable
/**
* Calculate the longest initial Iterable for which all element satisfy the specified predicate, creating a new `Iterable`.
*
* @category getters
* @since 2.0.0
*/
(self: Iterable, refinement: (a: A, i: number) => a is B): Iterable
/**
* Calculate the longest initial Iterable for which all element satisfy the specified predicate, creating a new `Iterable`.
*
* @category getters
* @since 2.0.0
*/
(self: Iterable, predicate: (a: A, i: number) => boolean): Iterable
} = dual(2, (self: Iterable, predicate: (a: A, i: number) => boolean): Iterable => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let i = 0
return {
next() {
const result = iterator.next()
if (result.done || !predicate(result.value, i++)) {
return { done: true, value: undefined }
}
return result
}
}
}
}))
/**
* Drop a max number of elements from the start of an `Iterable`
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
export const drop: {
/**
* Drop a max number of elements from the start of an `Iterable`
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
(n: number): (self: Iterable) => Iterable
/**
* Drop a max number of elements from the start of an `Iterable`
*
* **Note**. `n` is normalized to a non negative integer.
*
* @category getters
* @since 2.0.0
*/
(self: Iterable, n: number): Iterable
} = dual(2, (self: Iterable, n: number): Iterable => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let i = 0
return {
next() {
while (i < n) {
const result = iterator.next()
if (result.done) {
return { done: true, value: undefined }
}
i++
}
return iterator.next()
}
}
}
}))
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
export const findFirst: {
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(f: (a: NoInfer, i: number) => Option): (self: Iterable) => Option
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(refinement: (a: NoInfer, i: number) => a is B): (self: Iterable) => Option
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(predicate: (a: NoInfer, i: number) => boolean): (self: Iterable) => Option
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, f: (a: A, i: number) => Option): Option
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, refinement: (a: A, i: number) => a is B): Option
/**
* Returns the first element that satisfies the specified
* predicate, or `None` if no such element exists.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, predicate: (a: A, i: number) => boolean): Option
} = dual(
2,
(self: Iterable, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option)): Option => {
let i = 0
for (const a of self) {
const o = f(a, i)
if (isBoolean(o)) {
if (o) {
return O.some(a)
}
} else {
if (O.isSome(o)) {
return o
}
}
i++
}
return O.none()
}
)
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
export const findLast: {
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(f: (a: NoInfer, i: number) => Option): (self: Iterable) => Option
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(refinement: (a: NoInfer, i: number) => a is B): (self: Iterable) => Option
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(predicate: (a: NoInfer, i: number) => boolean): (self: Iterable) => Option
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, f: (a: A, i: number) => Option): Option
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, refinement: (a: A, i: number) => a is B): Option
/**
* Find the last element for which a predicate holds.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, predicate: (a: A, i: number) => boolean): Option
} = dual(
2,
(self: Iterable, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option)): Option => {
let i = 0
let last: Option = O.none()
for (const a of self) {
const o = f(a, i)
if (isBoolean(o)) {
if (o) {
last = O.some(a)
}
} else {
if (O.isSome(o)) {
last = o
}
}
i++
}
return last
}
)
/**
* Takes two `Iterable`s and returns an `Iterable` of corresponding pairs.
*
* @category zipping
* @since 2.0.0
*/
export const zip: {
/**
* Takes two `Iterable`s and returns an `Iterable` of corresponding pairs.
*
* @category zipping
* @since 2.0.0
*/
(that: Iterable): (self: Iterable) => Iterable<[A, B]>
/**
* Takes two `Iterable`s and returns an `Iterable` of corresponding pairs.
*
* @category zipping
* @since 2.0.0
*/
(self: Iterable, that: Iterable): Iterable<[A, B]>
} = dual(
2,
(self: Iterable, that: Iterable): Iterable<[A, B]> => zipWith(self, that, Tuple.make)
)
/**
* Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results. If one
* input `Iterable` is short, excess elements of the longer `Iterable` are discarded.
*
* @category zipping
* @since 2.0.0
*/
export const zipWith: {
/**
* Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results. If one
* input `Iterable` is short, excess elements of the longer `Iterable` are discarded.
*
* @category zipping
* @since 2.0.0
*/
(that: Iterable, f: (a: A, b: B) => C): (self: Iterable) => Iterable
/**
* Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results. If one
* input `Iterable` is short, excess elements of the longer `Iterable` are discarded.
*
* @category zipping
* @since 2.0.0
*/
(self: Iterable, that: Iterable, f: (a: A, b: B) => C): Iterable
} = dual(3, (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Iterable => ({
[Symbol.iterator]() {
const selfIterator = self[Symbol.iterator]()
const thatIterator = that[Symbol.iterator]()
return {
next() {
const selfResult = selfIterator.next()
const thatResult = thatIterator.next()
if (selfResult.done || thatResult.done) {
return { done: true, value: undefined }
}
return { done: false, value: f(selfResult.value, thatResult.value) }
}
}
}
}))
/**
* Places an element in between members of an `Iterable`.
* If the input is a non-empty array, the result is also a non-empty array.
*
* @since 2.0.0
*/
export const intersperse: {
/**
* Places an element in between members of an `Iterable`.
* If the input is a non-empty array, the result is also a non-empty array.
*
* @since 2.0.0
*/
(middle: B): (self: Iterable) => Iterable
/**
* Places an element in between members of an `Iterable`.
* If the input is a non-empty array, the result is also a non-empty array.
*
* @since 2.0.0
*/
(self: Iterable, middle: B): Iterable
} = dual(2, (self: Iterable, middle: B): Iterable => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let next = iterator.next()
let emitted = false
return {
next() {
if (next.done) {
return next
} else if (emitted) {
emitted = false
return { done: false, value: middle }
}
emitted = true
const result = next
next = iterator.next()
return result
}
}
}
}))
/**
* Returns a function that checks if an `Iterable` contains a given value using a provided `isEquivalent` function.
*
* @category elements
* @since 2.0.0
*/
export const containsWith = (isEquivalent: (self: A, that: A) => boolean): {
(a: A): (self: Iterable) => boolean
(self: Iterable, a: A): boolean
} =>
dual(2, (self: Iterable, a: A): boolean => {
for (const i of self) {
if (isEquivalent(a, i)) {
return true
}
}
return false
})
const _equivalence = Equal.equivalence()
/**
* Returns a function that checks if a `Iterable` contains a given value using the default `Equivalence`.
*
* @category elements
* @since 2.0.0
*/
export const contains: {
/**
* Returns a function that checks if a `Iterable` contains a given value using the default `Equivalence`.
*
* @category elements
* @since 2.0.0
*/
(a: A): (self: Iterable) => boolean
/**
* Returns a function that checks if a `Iterable` contains a given value using the default `Equivalence`.
*
* @category elements
* @since 2.0.0
*/
(self: Iterable, a: A): boolean
} = containsWith(_equivalence)
/**
* Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of
* the `Iterable`.
*
* @category splitting
* @since 2.0.0
*/
export const chunksOf: {
/**
* Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of
* the `Iterable`.
*
* @category splitting
* @since 2.0.0
*/
(n: number): (self: Iterable) => Iterable>
/**
* Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of
* the `Iterable`.
*
* @category splitting
* @since 2.0.0
*/
(self: Iterable, n: number): Iterable>
} = dual(2, (self: Iterable, n: number): Iterable> => {
const safeN = Math.max(1, Math.floor(n))
return ({
[Symbol.iterator]() {
let iterator: Iterator | undefined = self[Symbol.iterator]()
return {
next() {
if (iterator === undefined) {
return { done: true, value: undefined }
}
const chunk: Array = []
for (let i = 0; i < safeN; i++) {
const result = iterator.next()
if (result.done) {
iterator = undefined
return chunk.length === 0 ? { done: true, value: undefined } : { done: false, value: chunk }
}
chunk.push(result.value)
}
return { done: false, value: chunk }
}
}
}
})
})
/**
* Group equal, consecutive elements of an `Iterable` into `NonEmptyArray`s using the provided `isEquivalent` function.
*
* @category grouping
* @since 2.0.0
*/
export const groupWith: {
/**
* Group equal, consecutive elements of an `Iterable` into `NonEmptyArray`s using the provided `isEquivalent` function.
*
* @category grouping
* @since 2.0.0
*/
(isEquivalent: (self: A, that: A) => boolean): (self: Iterable) => Iterable>
/**
* Group equal, consecutive elements of an `Iterable` into `NonEmptyArray`s using the provided `isEquivalent` function.
*
* @category grouping
* @since 2.0.0
*/
(self: Iterable, isEquivalent: (self: A, that: A) => boolean): Iterable>
} = dual(
2,
(self: Iterable, isEquivalent: (self: A, that: A) => boolean): Iterable> => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let nextResult: IteratorResult | undefined
return {
next() {
let result: IteratorResult
if (nextResult !== undefined) {
if (nextResult.done) {
return { done: true, value: undefined }
}
result = nextResult
nextResult = undefined
} else {
result = iterator.next()
if (result.done) {
return { done: true, value: undefined }
}
}
const chunk: NonEmptyArray = [result.value]
while (true) {
const next = iterator.next()
if (next.done || !isEquivalent(result.value, next.value)) {
nextResult = next
return { done: false, value: chunk }
}
chunk.push(next.value)
}
}
}
}
})
)
/**
* Group equal, consecutive elements of an `Iterable` into `NonEmptyArray`s.
*
* @category grouping
* @since 2.0.0
*/
export const group: (self: Iterable) => Iterable> = groupWith(
Equal.equivalence()
)
/**
* Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning
* function on each element, and grouping the results according to values returned
*
* @category grouping
* @since 2.0.0
*/
export const groupBy: {
/**
* Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning
* function on each element, and grouping the results according to values returned
*
* @category grouping
* @since 2.0.0
*/
(f: (a: A) => K): (self: Iterable) => Record, NonEmptyArray>
/**
* Splits an `Iterable` into sub-non-empty-arrays stored in an object, based on the result of calling a `string`-returning
* function on each element, and grouping the results according to values returned
*
* @category grouping
* @since 2.0.0
*/
(self: Iterable, f: (a: A) => K): Record, NonEmptyArray>
} = dual(2, (
self: Iterable,
f: (a: A) => K
): Record, NonEmptyArray> => {
const out: Record> = {}
for (const a of self) {
const k = f(a)
if (Object.prototype.hasOwnProperty.call(out, k)) {
out[k].push(a)
} else {
out[k] = [a]
}
}
return out
})
const constEmpty: Iterable = {
[Symbol.iterator]() {
return constEmptyIterator
}
}
const constEmptyIterator: Iterator = {
next() {
return { done: true, value: undefined }
}
}
/**
* @category constructors
* @since 2.0.0
*/
export const empty = (): Iterable => constEmpty
/**
* Constructs a new `Iterable` from the specified value.
*
* @category constructors
* @since 2.0.0
*/
export const of = (a: A): Iterable => [a]
/**
* @category mapping
* @since 2.0.0
*/
export const map: {
/**
* @category mapping
* @since 2.0.0
*/
(f: (a: NoInfer, i: number) => B): (self: Iterable) => Iterable
/**
* @category mapping
* @since 2.0.0
*/
(self: Iterable, f: (a: NoInfer, i: number) => B): Iterable
} = dual(2, (self: Iterable, f: (a: A, i: number) => B): Iterable => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let i = 0
return {
next() {
const result = iterator.next()
if (result.done) {
return { done: true, value: undefined }
}
return { done: false, value: f(result.value, i++) }
}
}
}
}))
/**
* Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.
*
* @category sequencing
* @since 2.0.0
*/
export const flatMap: {
/**
* Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.
*
* @category sequencing
* @since 2.0.0
*/
(f: (a: NoInfer, i: number) => Iterable): (self: Iterable) => Iterable
/**
* Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.
*
* @category sequencing
* @since 2.0.0
*/
(self: Iterable, f: (a: NoInfer, i: number) => Iterable): Iterable
} = dual(
2,
(self: Iterable, f: (a: A, i: number) => Iterable): Iterable => flatten(map(self, f))
)
/**
* Flattens an Iterable of Iterables into a single Iterable
*
* @category sequencing
* @since 2.0.0
*/
export const flatten = (self: Iterable>): Iterable => ({
[Symbol.iterator]() {
const outerIterator = self[Symbol.iterator]()
let innerIterator: Iterator | undefined
function next() {
if (innerIterator === undefined) {
const next = outerIterator.next()
if (next.done) {
return next
}
innerIterator = next.value[Symbol.iterator]()
}
const result = innerIterator.next()
if (result.done) {
innerIterator = undefined
return next()
}
return result
}
return { next }
}
})
/**
* @category filtering
* @since 2.0.0
*/
export const filterMap: {
/**
* @category filtering
* @since 2.0.0
*/
(f: (a: A, i: number) => Option): (self: Iterable) => Iterable
/**
* @category filtering
* @since 2.0.0
*/
(self: Iterable, f: (a: A, i: number) => Option): Iterable
} = dual(
2,
(self: Iterable, f: (a: A, i: number) => Option): Iterable => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let i = 0
return {
next() {
let result = iterator.next()
while (!result.done) {
const b = f(result.value, i++)
if (O.isSome(b)) {
return { done: false, value: b.value }
}
result = iterator.next()
}
return { done: true, value: undefined }
}
}
}
})
)
/**
* Transforms all elements of the `Iterable` for as long as the specified function returns some value
*
* @category filtering
* @since 2.0.0
*/
export const filterMapWhile: {
/**
* Transforms all elements of the `Iterable` for as long as the specified function returns some value
*
* @category filtering
* @since 2.0.0
*/
(f: (a: A, i: number) => Option): (self: Iterable) => Iterable
/**
* Transforms all elements of the `Iterable` for as long as the specified function returns some value
*
* @category filtering
* @since 2.0.0
*/
(self: Iterable, f: (a: A, i: number) => Option): Iterable
} = dual(2, (self: Iterable, f: (a: A, i: number) => Option) => ({
[Symbol.iterator]() {
const iterator = self[Symbol.iterator]()
let i = 0
return {
next() {
const result = iterator.next()
if (result.done) {
return { done: true, value: undefined }
}
const b = f(result.value, i++)
if (O.isSome(b)) {
return { done: false, value: b.value }
}
return { done: true, value: undefined }
}
}
}
}))
/**
* Retrieves the `Some` values from an `Iterable` of `Option`s.
*
* @example
* ```ts
* import * as assert from "node:assert"
* import { Iterable, Option } from "effect"
*
* assert.deepStrictEqual(
* Array.from(Iterable.getSomes([Option.some(1), Option.none(), Option.some(2)])),
* [1, 2]
* )
* ```
*
* @category filtering
* @since 2.0.0
*/
export const getSomes: (self: Iterable