/** * This module provides utility functions for working with arrays in TypeScript. * * @since 1.0.0 */ import type { Either } from "@effect/data/Either" import * as E from "@effect/data/Either" import * as Equal from "@effect/data/Equal" import * as Equivalence from "@effect/data/Equivalence" import { dual, identity } from "@effect/data/Function" import type { LazyArg } from "@effect/data/Function" import type { TypeLambda } from "@effect/data/HKT" import * as readonlyArray from "@effect/data/internal/ReadonlyArray" import type { Option } from "@effect/data/Option" import * as O from "@effect/data/Option" import * as Order from "@effect/data/Order" import type { Predicate, Refinement } from "@effect/data/Predicate" import * as RR from "@effect/data/ReadonlyRecord" /** * @category type lambdas * @since 1.0.0 */ export interface ReadonlyArrayTypeLambda extends TypeLambda { readonly type: ReadonlyArray } /** * @category models * @since 1.0.0 */ export type NonEmptyReadonlyArray = readonly [A, ...Array] /** * @category models * @since 1.0.0 */ export type NonEmptyArray = [A, ...Array] /** * Builds a `NonEmptyArray` from an non-empty collection of elements. * * @category constructors * @since 1.0.0 */ export const make = >( ...elements: Elements ): NonEmptyArray => elements /** * Return a `NonEmptyArray` of length `n` with element `i` initialized with `f(i)`. * * **Note**. `n` is normalized to an integer >= 1. * * @example * import { makeBy } from '@effect/data/ReadonlyArray' * * assert.deepStrictEqual(makeBy(5, n => n * 2), [0, 2, 4, 6, 8]) * * @category constructors * @since 1.0.0 */ export const makeBy = (n: number, f: (i: number) => A): NonEmptyArray => { const max = Math.max(1, Math.floor(n)) const out: NonEmptyArray = [f(0)] for (let i = 1; i < max; i++) { out.push(f(i)) } return out } /** * Return a `NonEmptyArray` containing a range of integers, including both endpoints. * * @example * import { range } from '@effect/data/ReadonlyArray' * * assert.deepStrictEqual(range(1, 3), [1, 2, 3]) * * @category constructors * @since 1.0.0 */ export const range = (start: number, end: number): NonEmptyArray => start <= end ? makeBy(end - start + 1, (i) => start + i) : [start] /** * Return a `NonEmptyArray` containing a value repeated the specified number of times. * * **Note**. `n` is normalized to an integer >= 1. * * @example * import { replicate } from '@effect/data/ReadonlyArray' * * assert.deepStrictEqual(replicate("a", 3), ["a", "a", "a"]) * * @category constructors * @since 1.0.0 */ export const replicate: { (n: number): (a: A) => NonEmptyArray (a: A, n: number): NonEmptyArray } = dual(2, (a: A, n: number): NonEmptyArray => makeBy(n, () => a)) /** * @category conversions * @since 1.0.0 */ export const fromIterable = (collection: Iterable): Array => Array.isArray(collection) ? collection : Array.from(collection) /** * Takes a record and returns an array of tuples containing its keys and values. * * @param self - The record to transform. * * @example * import { fromRecord } from "@effect/data/ReadonlyArray" * * const x = { a: 1, b: 2, c: 3 } * assert.deepStrictEqual(fromRecord(x), [["a", 1], ["b", 2], ["c", 3]]) * * @category conversions * @since 1.0.0 */ export const fromRecord: (self: Readonly>) => Array<[K, A]> = RR.toEntries /** * @category conversions * @since 1.0.0 */ export const fromOption: (self: Option) => Array = O.toArray /** * @category pattern matching * @since 1.0.0 */ export const match: { ( options: { readonly onEmpty: LazyArg readonly onNonEmpty: (self: NonEmptyReadonlyArray) => C } ): (self: ReadonlyArray) => B | C ( self: ReadonlyArray, options: { readonly onEmpty: LazyArg readonly onNonEmpty: (self: NonEmptyReadonlyArray) => C } ): B | C } = dual(2, ( self: ReadonlyArray, { onEmpty, onNonEmpty }: { readonly onEmpty: LazyArg readonly onNonEmpty: (self: NonEmptyReadonlyArray) => C } ): B | C => isNonEmptyReadonlyArray(self) ? onNonEmpty(self) : onEmpty()) /** * @category pattern matching * @since 1.0.0 */ export const matchLeft: { ( options: { readonly onEmpty: LazyArg readonly onNonEmpty: (head: A, tail: Array) => C } ): (self: ReadonlyArray) => B | C ( self: ReadonlyArray, options: { readonly onEmpty: LazyArg readonly onNonEmpty: (head: A, tail: Array) => C } ): B | C } = dual(2, ( self: ReadonlyArray, { onEmpty, onNonEmpty }: { readonly onEmpty: LazyArg readonly onNonEmpty: (head: A, tail: Array) => C } ): B | C => isNonEmptyReadonlyArray(self) ? onNonEmpty(headNonEmpty(self), tailNonEmpty(self)) : onEmpty()) /** * @category pattern matching * @since 1.0.0 */ export const matchRight: { ( options: { readonly onEmpty: LazyArg readonly onNonEmpty: (init: Array, last: A) => C } ): (self: ReadonlyArray) => B | C ( self: ReadonlyArray, options: { readonly onEmpty: LazyArg readonly onNonEmpty: (init: Array, last: A) => C } ): B | C } = dual(2, ( self: ReadonlyArray, { onEmpty, onNonEmpty }: { readonly onEmpty: LazyArg readonly onNonEmpty: (init: Array, last: A) => C } ): B | C => isNonEmptyReadonlyArray(self) ? onNonEmpty(initNonEmpty(self), lastNonEmpty(self)) : onEmpty()) /** * Prepend an element to the front of an `Iterable`, creating a new `NonEmptyArray`. * * @category concatenating * @since 1.0.0 */ export const prepend: { (head: B): (self: Iterable) => NonEmptyArray (self: Iterable, head: B): NonEmptyArray } = dual(2, (self: Iterable, head: B): NonEmptyArray => [head, ...self]) /** * @category concatenating * @since 1.0.0 */ export const prependAll: { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } = dual( 2, (self: Iterable, that: Iterable): Array => fromIterable(that).concat(fromIterable(self)) ) /** * @category concatenating * @since 1.0.0 */ export const prependAllNonEmpty: { (that: NonEmptyReadonlyArray): (self: Iterable) => NonEmptyArray (that: Iterable): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: Iterable, that: NonEmptyReadonlyArray): NonEmptyArray (self: NonEmptyReadonlyArray, that: Iterable): NonEmptyArray } = dual( 2, (self: Iterable, that: Iterable): Array => prependAll(self, that) ) /** * Append an element to the end of an `Iterable`, creating a new `NonEmptyArray`. * * @category concatenating * @since 1.0.0 */ export const append: { (last: B): (self: Iterable) => NonEmptyArray (self: Iterable, last: B): NonEmptyArray } = dual(2, (self: Iterable, last: B): Array => [...self, last]) /** * @category concatenating * @since 1.0.0 */ export const appendAll: { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } = dual( 2, (self: Iterable, that: Iterable): Array => fromIterable(self).concat(fromIterable(that)) ) /** * @category concatenating * @since 1.0.0 */ export const appendAllNonEmpty: { (that: NonEmptyReadonlyArray): (self: Iterable) => NonEmptyArray (that: Iterable): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: Iterable, that: NonEmptyReadonlyArray): NonEmptyArray (self: NonEmptyReadonlyArray, that: Iterable): NonEmptyArray } = dual( 2, (self: Iterable, that: Iterable): Array => appendAll(self, that) ) /** * Reduce an `Iterable` from the left, keeping all intermediate results instead of only the final result. * * @category folding * @since 1.0.0 */ export const scan: { (b: B, f: (b: B, a: A) => B): (self: Iterable) => NonEmptyArray (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray } = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray => { const out: NonEmptyArray = [b] let i = 0 for (const a of self) { out[i + 1] = f(out[i], a) i++ } return out }) /** * Reduce an `Iterable` from the right, keeping all intermediate results instead of only the final result. * * @category folding * @since 1.0.0 */ export const scanRight: { (b: B, f: (b: B, a: A) => B): (self: Iterable) => NonEmptyArray (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray } = dual(3, (self: Iterable, b: B, f: (b: B, a: A) => B): NonEmptyArray => { const input = fromIterable(self) const out: NonEmptyArray = new Array(input.length + 1) as any out[input.length] = b for (let i = input.length - 1; i >= 0; i--) { out[i] = f(out[i + 1], input[i]) } return out }) /** * Determine if an `Array` is empty narrowing down the type to `[]`. * * @param self - The `Array` to check. * * @example * import { isEmptyArray } from "@effect/data/ReadonlyArray" * * assert.deepStrictEqual(isEmptyArray([]), true); * assert.deepStrictEqual(isEmptyArray([1, 2, 3]), false); * * @category guards * @since 1.0.0 */ export const isEmptyArray = (self: Array): self is [] => self.length === 0 /** * Determine if a `ReadonlyArray` is empty narrowing down the type to `readonly []`. * * @param self - The `ReadonlyArray` to check. * * @example * import { isEmptyReadonlyArray } from "@effect/data/ReadonlyArray" * * assert.deepStrictEqual(isEmptyReadonlyArray([]), true); * assert.deepStrictEqual(isEmptyReadonlyArray([1, 2, 3]), false); * * @category guards * @since 1.0.0 */ export const isEmptyReadonlyArray: (self: ReadonlyArray) => self is readonly [] = isEmptyArray as any /** * Determine if an `Array` is non empty narrowing down the type to `NonEmptyArray`. * * An `Array` is considered to be a `NonEmptyArray` if it contains at least one element. * * @param self - The `Array` to check. * * @example * import { isNonEmptyArray } from "@effect/data/ReadonlyArray" * * assert.deepStrictEqual(isNonEmptyArray([]), false); * assert.deepStrictEqual(isNonEmptyArray([1, 2, 3]), true); * * @category guards * @since 1.0.0 */ export const isNonEmptyArray: (self: Array) => self is NonEmptyArray = readonlyArray.isNonEmptyArray /** * Determine if a `ReadonlyArray` is non empty narrowing down the type to `NonEmptyReadonlyArray`. * * A `ReadonlyArray` is considered to be a `NonEmptyReadonlyArray` if it contains at least one element. * * @param self - The `ReadonlyArray` to check. * * @example * import { isNonEmptyReadonlyArray } from "@effect/data/ReadonlyArray" * * assert.deepStrictEqual(isNonEmptyReadonlyArray([]), false); * assert.deepStrictEqual(isNonEmptyReadonlyArray([1, 2, 3]), true); * * @category guards * @since 1.0.0 */ export const isNonEmptyReadonlyArray: (self: ReadonlyArray) => self is NonEmptyReadonlyArray = readonlyArray.isNonEmptyArray /** * Return the number of elements in a `ReadonlyArray`. * * @category getters * @since 1.0.0 */ export const length = (self: ReadonlyArray): number => self.length const isOutOfBound = (i: number, as: ReadonlyArray): boolean => i < 0 || i >= as.length const clamp = (i: number, as: ReadonlyArray): number => Math.floor(Math.min(Math.max(0, i), as.length)) /** * This function provides a safe way to read a value at a particular index from a `ReadonlyArray`. * * @category getters * @since 1.0.0 */ export const get: { (index: number): (self: ReadonlyArray) => Option (self: ReadonlyArray, index: number): Option } = dual(2, (self: ReadonlyArray, index: number): Option => { const i = Math.floor(index) return isOutOfBound(i, self) ? O.none() : O.some(self[i]) }) /** * Gets an element unsafely, will throw on out of bounds. * * @since 1.0.0 * @category unsafe */ export const unsafeGet: { (index: number): (self: ReadonlyArray) => A (self: ReadonlyArray, index: number): A } = dual(2, (self: ReadonlyArray, index: number): A => { const i = Math.floor(index) if (isOutOfBound(i, self)) { throw new Error(`Index ${i} out of bounds`) } return self[i] }) /** * Return a tuple containing the first element, and a new `Array` of the remaining elements, if any. * * @category getters * @since 1.0.0 */ export const unprepend = ( self: NonEmptyReadonlyArray ): [A, Array] => [headNonEmpty(self), tailNonEmpty(self)] /** * Return a tuple containing a copy of the `NonEmptyReadonlyArray` without its last element, and that last element. * * @category getters * @since 1.0.0 */ export const unappend = ( self: NonEmptyReadonlyArray ): [Array, A] => [initNonEmpty(self), lastNonEmpty(self)] /** * Get the first element of a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. * * @category getters * @since 1.0.0 */ export const head: (self: ReadonlyArray) => Option = get(0) /** * @category getters * @since 1.0.0 */ export const headNonEmpty: (self: NonEmptyReadonlyArray) => A = unsafeGet(0) /** * Get the last element in a `ReadonlyArray`, or `None` if the `ReadonlyArray` is empty. * * @category getters * @since 1.0.0 */ export const last = (self: ReadonlyArray): Option => isNonEmptyReadonlyArray(self) ? O.some(lastNonEmpty(self)) : O.none() /** * @category getters * @since 1.0.0 */ export const lastNonEmpty = (self: NonEmptyReadonlyArray): A => self[self.length - 1] /** * Get all but the first element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. * * @category getters * @since 1.0.0 */ export const tail = (self: Iterable): Option> => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? O.some(tailNonEmpty(input)) : O.none() } /** * @category getters * @since 1.0.0 */ export const tailNonEmpty = (self: NonEmptyReadonlyArray): Array => self.slice(1) /** * Get all but the last element of an `Iterable`, creating a new `Array`, or `None` if the `Iterable` is empty. * * @category getters * @since 1.0.0 */ export const init = (self: Iterable): Option> => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? O.some(initNonEmpty(input)) : O.none() } /** * Get all but the last element of a non empty array, creating a new array. * * @category getters * @since 1.0.0 */ export const initNonEmpty = (self: NonEmptyReadonlyArray): Array => self.slice(0, -1) /** * Keep only a max number of elements from the start of an `Iterable`, creating a new `Array`. * * **Note**. `n` is normalized to a non negative integer. * * @category getters * @since 1.0.0 */ export const take: { (n: number): (self: Iterable) => Array (self: Iterable, n: number): Array } = dual(2, (self: Iterable, n: number): Array => { const input = fromIterable(self) return input.slice(0, clamp(n, input)) }) /** * Keep only a max number of elements from the end of an `Iterable`, creating a new `Array`. * * **Note**. `n` is normalized to a non negative integer. * * @category getters * @since 1.0.0 */ export const takeRight: { (n: number): (self: Iterable) => Array (self: Iterable, n: number): Array } = dual(2, (self: Iterable, n: number): Array => { const input = fromIterable(self) const i = clamp(n, input) return i === 0 ? [] : input.slice(-i) }) /** * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. * * @category getters * @since 1.0.0 */ export const takeWhile: { (refinement: Refinement): (self: Iterable) => Array (predicate: Predicate): (self: Iterable) => Array (self: Iterable, refinement: Refinement): Array (self: Iterable, predicate: Predicate): Array } = dual(2, (self: Iterable, predicate: Predicate): Array => { const out: Array = [] for (const a of self) { if (!predicate(a)) { break } out.push(a) } return out }) const spanIndex = (self: Iterable, predicate: Predicate): number => { let i = 0 for (const a of self) { if (!predicate(a)) { break } i++ } return i } /** * Split an `Iterable` into two parts: * * 1. the longest initial subarray for which all elements satisfy the specified predicate * 2. the remaining elements * * @category filtering * @since 1.0.0 */ export const span: { ( refinement: Refinement ): (self: Iterable) => [init: Array, rest: Array] (predicate: Predicate): (self: Iterable) => [init: Array, rest: Array] ( self: Iterable, refinement: Refinement ): [init: Array, rest: Array] (self: Iterable, predicate: Predicate): [init: Array, rest: Array] } = dual( 2, (self: Iterable, predicate: Predicate): [init: Array, rest: Array] => splitAt(self, spanIndex(self, predicate)) ) /** * Drop a max number of elements from the start of an `Iterable`, creating a new `Array`. * * **Note**. `n` is normalized to a non negative integer. * * @category getters * @since 1.0.0 */ export const drop: { (n: number): (self: Iterable) => Array (self: Iterable, n: number): Array } = dual(2, (self: Iterable, n: number): Array => { const input = fromIterable(self) return input.slice(clamp(n, input), input.length) }) /** * Drop a max number of elements from the end of an `Iterable`, creating a new `Array`. * * **Note**. `n` is normalized to a non negative integer. * * @category getters * @since 1.0.0 */ export const dropRight: { (n: number): (self: Iterable) => Array (self: Iterable, n: number): Array } = dual(2, (self: Iterable, n: number): Array => { const input = fromIterable(self) return input.slice(0, input.length - clamp(n, input)) }) /** * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new `Array`. * * @category getters * @since 1.0.0 */ export const dropWhile: { (refinement: Refinement): (self: Iterable) => Array (predicate: Predicate): (self: Iterable) => Array (self: Iterable, refinement: Refinement): Array (self: Iterable, predicate: Predicate): Array } = dual( 2, (self: Iterable, predicate: Predicate): Array => fromIterable(self).slice(spanIndex(self, predicate)) ) /** * Return the first index for which a predicate holds. * * @category elements * @since 1.0.0 */ export const findFirstIndex: { (predicate: Predicate): (self: Iterable) => Option (self: Iterable, predicate: Predicate): Option } = dual(2, (self: Iterable, predicate: Predicate): Option => { let i = 0 for (const a of self) { if (predicate(a)) { return O.some(i) } i++ } return O.none() }) /** * Return the last index for which a predicate holds. * * @category elements * @since 1.0.0 */ export const findLastIndex: { (predicate: Predicate): (self: Iterable) => Option (self: Iterable, predicate: Predicate): Option } = dual(2, (self: Iterable, predicate: Predicate): Option => { const input = fromIterable(self) for (let i = input.length - 1; i >= 0; i--) { if (predicate(input[i])) { return O.some(i) } } return O.none() }) /** * Returns the first element that satisfies the specified * predicate, or `None` if no such element exists. * * @category elements * @since 1.0.0 */ export const findFirst: { (refinement: Refinement): (self: Iterable) => Option (predicate: Predicate): (self: Iterable) => Option (self: Iterable, refinement: Refinement): Option (self: Iterable, predicate: Predicate): Option } = dual(2, (self: Iterable, predicate: Predicate): Option => { const input = fromIterable(self) for (let i = 0; i < input.length; i++) { if (predicate(input[i])) { return O.some(input[i]) } } return O.none() }) /** * Find the last element for which a predicate holds. * * @category elements * @since 1.0.0 */ export const findLast: { (refinement: Refinement): (self: Iterable) => Option (predicate: Predicate): (self: Iterable) => Option (self: Iterable, refinement: Refinement): Option (self: Iterable, predicate: Predicate): Option } = dual(2, (self: Iterable, predicate: Predicate): Option => { const input = fromIterable(self) for (let i = input.length - 1; i >= 0; i--) { if (predicate(input[i])) { return O.some(input[i]) } } return O.none() }) /** * Insert an element at the specified index, creating a new `NonEmptyArray`, * or return `None` if the index is out of bounds. * * @since 1.0.0 */ export const insertAt: { (i: number, b: B): (self: Iterable) => Option> (self: Iterable, i: number, b: B): Option> } = dual(3, (self: Iterable, i: number, b: B): Option> => { const out: Array = Array.from(self) // v--- `= self.length` is ok, it means inserting in last position if (i < 0 || i > out.length) { return O.none() } out.splice(i, 0, b) return O.some(out) as any }) /** * Change the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * * @since 1.0.0 */ export const replace: { (i: number, b: B): (self: Iterable) => Array (self: Iterable, i: number, b: B): Array } = dual(3, (self: Iterable, i: number, b: B): Array => modify(self, i, () => b)) /** * @since 1.0.0 */ export const replaceOption: { (i: number, b: B): (self: Iterable) => Option> (self: Iterable, i: number, b: B): Option> } = dual( 3, (self: Iterable, i: number, b: B): Option> => modifyOption(self, i, () => b) ) /** * Apply a function to the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * * @since 1.0.0 */ export const modify: { (i: number, f: (a: A) => B): (self: Iterable) => Array (self: Iterable, i: number, f: (a: A) => B): Array } = dual( 3, (self: Iterable, i: number, f: (a: A) => B): Array => O.getOrElse(modifyOption(self, i, f), () => Array.from(self)) ) /** * Apply a function to the element at the specified index, creating a new `Array`, * or return `None` if the index is out of bounds. * * @since 1.0.0 */ export const modifyOption: { (i: number, f: (a: A) => B): (self: Iterable) => Option> (self: Iterable, i: number, f: (a: A) => B): Option> } = dual(3, (self: Iterable, i: number, f: (a: A) => B): Option> => { const out = Array.from(self) if (isOutOfBound(i, out)) { return O.none() } const next = f(out[i]) // @ts-expect-error out[i] = next return O.some(out) }) /** * Delete the element at the specified index, creating a new `Array`, * or return a copy of the input if the index is out of bounds. * * @since 1.0.0 */ export const remove: { (i: number): (self: Iterable) => Array (self: Iterable, i: number): Array } = dual(2, (self: Iterable, i: number): Array => { const out = Array.from(self) if (isOutOfBound(i, out)) { return out } out.splice(i, 1) return out }) /** * Reverse an `Iterable`, creating a new `Array`. * * @category elements * @since 1.0.0 */ export const reverse = (self: Iterable): Array => Array.from(self).reverse() /** * @category elements * @since 1.0.0 */ export const reverseNonEmpty = ( self: NonEmptyReadonlyArray ): NonEmptyArray => [lastNonEmpty(self), ...self.slice(0, -1).reverse()] /** * Sort the elements of an `Iterable` in increasing order, creating a new `Array`. * * @category sorting * @since 1.0.0 */ export const sort: { (O: Order.Order): (self: Iterable) => Array (self: Iterable, O: Order.Order): Array } = dual(2, (self: Iterable, O: Order.Order): Array => { const out = Array.from(self) out.sort(O) return out }) /** * @since 1.0.0 * @category elements */ export const sortWith: { (f: (a: A) => B, order: Order.Order): (self: ReadonlyArray) => Array (self: ReadonlyArray, f: (a: A) => B, order: Order.Order): Array } = dual( 3, (self: ReadonlyArray, f: (a: A) => B, order: Order.Order): Array => sort(self, Order.mapInput(order, f)) ) /** * Sort the elements of a `NonEmptyReadonlyArray` in increasing order, creating a new `NonEmptyArray`. * * @category sorting * @since 1.0.0 */ export const sortNonEmpty: { (O: Order.Order): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, O: Order.Order): NonEmptyArray } = dual( 2, (self: NonEmptyReadonlyArray, O: Order.Order): NonEmptyArray => sort(O)(self) as any ) /** * Sort the elements of an `Iterable` in increasing order, where elements are compared * using first `orders[0]`, then `orders[1]`, etc... * * @category sorting * @since 1.0.0 */ export const sortBy = (...orders: ReadonlyArray>) => (self: Iterable): Array => { const input = fromIterable(self) return (isNonEmptyReadonlyArray(input) ? sortByNonEmpty(...orders)(input) : []) } /** * @category sorting * @since 1.0.0 */ export const sortByNonEmpty = ( ...orders: ReadonlyArray> ): (as: NonEmptyReadonlyArray) => NonEmptyArray => sortNonEmpty(Order.combineAll(orders)) /** * Takes two `Iterable`s and returns an `Array` of corresponding pairs. * If one input `Iterable` is short, excess elements of the * longer `Iterable` are discarded. * * @since 1.0.0 */ export const zip: { (that: Iterable): (self: Iterable) => Array<[A, B]> (self: Iterable, that: Iterable): Array<[A, B]> } = dual( 2, (self: Iterable, that: Iterable): Array<[A, B]> => zipWith(self, that, (a, b) => [a, b]) ) /** * Apply a function to pairs of elements at the same index in two `Iterable`s, collecting the results in a new `Array`. If one * input `Iterable` is short, excess elements of the longer `Iterable` are discarded. * * @since 1.0.0 */ export const zipWith: { (that: Iterable, f: (a: A, b: B) => C): (self: Iterable) => Array (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Array } = dual(3, (self: Iterable, that: Iterable, f: (a: A, b: B) => C): Array => { const as = fromIterable(self) const bs = fromIterable(that) return isNonEmptyReadonlyArray(as) && isNonEmptyReadonlyArray(bs) ? zipNonEmptyWith(bs, f)(as) : [] }) /** * @since 1.0.0 */ export const zipNonEmpty: { (that: NonEmptyReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray<[A, B]> (self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> } = dual( 2, (self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray<[A, B]> => zipNonEmptyWith(self, that, (a, b) => [a, b]) ) /** * @since 1.0.0 */ export const zipNonEmptyWith: { ( that: NonEmptyReadonlyArray, f: (a: A, b: B) => C ): (self: NonEmptyReadonlyArray) => NonEmptyArray ( self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray, f: (a: A, b: B) => C ): NonEmptyArray } = dual(3, ( self: NonEmptyReadonlyArray, that: NonEmptyReadonlyArray, f: (a: A, b: B) => C ): NonEmptyArray => { const cs: NonEmptyArray = [f(headNonEmpty(self), headNonEmpty(that))] const len = Math.min(self.length, that.length) for (let i = 1; i < len; i++) { cs[i] = f(self[i], that[i]) } return cs }) /** * This function is the inverse of `zip`. Takes an `Iterable` of pairs and return two corresponding `Array`s. * * @since 1.0.0 */ export const unzip = (self: Iterable): [Array, Array] => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? unzipNonEmpty(input) : [[], []] } /** * @since 1.0.0 */ export const unzipNonEmpty = ( self: NonEmptyReadonlyArray ): [NonEmptyArray, NonEmptyArray] => { const fa: NonEmptyArray = [self[0][0]] const fb: NonEmptyArray = [self[0][1]] for (let i = 1; i < self.length; i++) { fa[i] = self[i][0] fb[i] = self[i][1] } return [fa, fb] } /** * Places an element in between members of an `Iterable` * * @since 1.0.0 */ export const intersperse: { (middle: B): (self: Iterable) => Array (self: Iterable, middle: B): Array } = dual(2, (self: Iterable, middle: B): Array => { const input = fromIterable(self) return (isNonEmptyReadonlyArray(input) ? intersperseNonEmpty(input, middle) : []) }) /** * Places an element in between members of a `NonEmptyReadonlyArray` * * @since 1.0.0 */ export const intersperseNonEmpty: { (middle: B): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, middle: B): NonEmptyArray } = dual(2, (self: NonEmptyReadonlyArray, middle: B): NonEmptyArray => { const out: NonEmptyArray = [headNonEmpty(self)] const tail = tailNonEmpty(self) for (let i = 0; i < tail.length; i++) { if (i < tail.length) { out.push(middle) } out.push(tail[i]) } return out }) /** * Apply a function to the head, creating a new `NonEmptyReadonlyArray`. * * @since 1.0.0 */ export const modifyNonEmptyHead: { (f: (a: A) => B): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray } = dual( 2, ( self: NonEmptyReadonlyArray, f: (a: A) => B ): NonEmptyArray => [f(headNonEmpty(self)), ...tailNonEmpty(self)] ) /** * Change the head, creating a new `NonEmptyReadonlyArray`. * * @since 1.0.0 */ export const setNonEmptyHead: { (b: B): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, b: B): NonEmptyArray } = dual( 2, (self: NonEmptyReadonlyArray, b: B): NonEmptyArray => modifyNonEmptyHead(self, () => b) ) /** * Apply a function to the last element, creating a new `NonEmptyReadonlyArray`. * * @since 1.0.0 */ export const modifyNonEmptyLast: { (f: (a: A) => B): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray } = dual( 2, (self: NonEmptyReadonlyArray, f: (a: A) => B): NonEmptyArray => append(initNonEmpty(self), f(lastNonEmpty(self))) ) /** * Change the last element, creating a new `NonEmptyReadonlyArray`. * * @since 1.0.0 */ export const setNonEmptyLast: { (b: B): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, b: B): NonEmptyArray } = dual( 2, (self: NonEmptyReadonlyArray, b: B): NonEmptyArray => modifyNonEmptyLast(self, () => b) ) /** * Rotate an `Iterable` by `n` steps. * * @since 1.0.0 */ export const rotate: { (n: number): (self: Iterable) => Array (self: Iterable, n: number): Array } = dual(2, (self: Iterable, n: number): Array => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? rotateNonEmpty(input, n) : [] }) /** * Rotate a `NonEmptyReadonlyArray` by `n` steps. * * @since 1.0.0 */ export const rotateNonEmpty: { (n: number): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, n: number): NonEmptyArray } = dual(2, (self: NonEmptyReadonlyArray, n: number): NonEmptyArray => { const len = self.length const m = Math.round(n) % len if (isOutOfBound(Math.abs(m), self) || m === 0) { return copy(self) } if (m < 0) { const [f, s] = splitNonEmptyAt(self, -m) return appendAllNonEmpty(s, f) } else { return rotateNonEmpty(self, m - len) } }) /** * Returns a function that checks if a `ReadonlyArray` contains a given value using a provided `isEquivalent` function. * * @category elements * @since 1.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 `ReadonlyArray` contains a given value using the default `Equivalence`. * * @category elements * @since 1.0.0 */ export const contains: { (a: A): (self: Iterable) => boolean (self: Iterable, a: A): boolean } = containsWith(_equivalence) /** * Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element using the provided `isEquivalent` function. * * @since 1.0.0 */ export const dedupeNonEmptyWith: { (isEquivalent: (self: A, that: A) => boolean): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, isEquivalent: (self: A, that: A) => boolean): NonEmptyArray } = dual(2, (self: NonEmptyReadonlyArray, isEquivalent: (self: A, that: A) => boolean): NonEmptyArray => { const out: NonEmptyArray = [headNonEmpty(self)] const rest = tailNonEmpty(self) for (const a of rest) { if (out.every((o) => !isEquivalent(a, o))) { out.push(a) } } return out }) /** * Remove duplicates from a `NonEmptyReadonlyArray`, keeping the first occurrence of an element. * * @since 1.0.0 */ export const dedupeNonEmpty: (self: NonEmptyReadonlyArray) => NonEmptyArray = dedupeNonEmptyWith( Equal.equivalence() ) /** * A useful recursion pattern for processing an `Iterable` to produce a new `Array`, often used for "chopping" up the input * `Iterable`. Typically chop is called with some function that will consume an initial prefix of the `Iterable` and produce a * value and the rest of the `Array`. * * @since 1.0.0 */ export const chop: { ( f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): (self: Iterable) => Array ( self: Iterable, f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): Array } = dual(2, ( self: Iterable, f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): Array => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? chopNonEmpty(input, f) : [] }) /** * A useful recursion pattern for processing a `NonEmptyReadonlyArray` to produce a new `NonEmptyReadonlyArray`, often used for "chopping" up the input * `NonEmptyReadonlyArray`. Typically `chop` is called with some function that will consume an initial prefix of the `NonEmptyReadonlyArray` and produce a * value and the tail of the `NonEmptyReadonlyArray`. * * @since 1.0.0 */ export const chopNonEmpty: { ( f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): (self: NonEmptyReadonlyArray) => NonEmptyArray ( self: NonEmptyReadonlyArray, f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): NonEmptyArray } = dual(2, ( self: NonEmptyReadonlyArray, f: (as: NonEmptyReadonlyArray) => readonly [B, ReadonlyArray] ): NonEmptyArray => { const [b, rest] = f(self) const out: NonEmptyArray = [b] let next: ReadonlyArray = rest while (readonlyArray.isNonEmptyArray(next)) { const [b, rest] = f(next) out.push(b) next = rest } return out }) /** * Splits an `Iterable` into two pieces, the first piece has max `n` elements. * * @category getters * @since 1.0.0 */ export const splitAt: { (n: number): (self: Iterable) => [Array, Array] (self: Iterable, n: number): [Array, Array] } = dual(2, (self: Iterable, n: number): [Array, Array] => { const input = Array.from(self) return n >= 1 && isNonEmptyReadonlyArray(input) ? splitNonEmptyAt(input, n) : isEmptyReadonlyArray(input) ? [input, []] : [[], input] }) /** * @since 1.0.0 */ export const copy: { (self: NonEmptyReadonlyArray): NonEmptyArray (self: ReadonlyArray): Array } = ((self: ReadonlyArray): Array => self.slice()) as any /** * Splits a `NonEmptyReadonlyArray` into two pieces, the first piece has max `n` elements. * * @category getters * @since 1.0.0 */ export const splitNonEmptyAt: { (n: number): (self: NonEmptyReadonlyArray) => [NonEmptyArray, Array] (self: NonEmptyReadonlyArray, n: number): [NonEmptyArray, Array] } = dual(2, (self: NonEmptyReadonlyArray, n: number): [NonEmptyArray, Array] => { const m = Math.max(1, n) return m >= self.length ? [copy(self), []] : [prepend(self.slice(1, m), headNonEmpty(self)), self.slice(m)] }) /** * Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of * the `Iterable`. Note that `chunksOf(n)([])` is `[]`, not `[[]]`. This is intentional, and is consistent with a recursive * definition of `chunksOf`; it satisfies the property that * * ```ts * chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys))) * ``` * * whenever `n` evenly divides the length of `self`. * * @category getters * @since 1.0.0 */ export const chunksOf: { (n: number): (self: Iterable) => Array> (self: Iterable, n: number): Array> } = dual(2, (self: Iterable, n: number): Array> => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? chunksOfNonEmpty(input, n) : [] }) /** * Splits a `NonEmptyReadonlyArray` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of * the `NonEmptyReadonlyArray`. * * @category getters * @since 1.0.0 */ export const chunksOfNonEmpty: { (n: number): (self: NonEmptyReadonlyArray) => NonEmptyArray> (self: NonEmptyReadonlyArray, n: number): NonEmptyArray> } = dual( 2, (self: NonEmptyReadonlyArray, n: number): NonEmptyArray> => chopNonEmpty(self, splitNonEmptyAt(n)) ) /** * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s using the provided `isEquivalent` function. * * @category grouping * @since 1.0.0 */ export const groupWith: { (isEquivalent: (self: A, that: A) => boolean): (self: NonEmptyReadonlyArray) => NonEmptyArray> (self: NonEmptyReadonlyArray, isEquivalent: (self: A, that: A) => boolean): NonEmptyArray> } = dual( 2, (self: NonEmptyReadonlyArray, isEquivalent: (self: A, that: A) => boolean): NonEmptyArray> => chopNonEmpty(self, (as) => { const h = headNonEmpty(as) const out: NonEmptyArray = [h] let i = 1 for (; i < as.length; i++) { const a = as[i] if (isEquivalent(a, h)) { out.push(a) } else { break } } return [out, as.slice(i)] }) ) /** * Group equal, consecutive elements of a `NonEmptyReadonlyArray` into `NonEmptyArray`s. * * @category grouping * @since 1.0.0 */ export const group: (self: NonEmptyReadonlyArray) => NonEmptyArray> = 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 1.0.0 */ export const groupBy: { (f: (a: A) => string): (self: Iterable) => Record> (self: Iterable, f: (a: A) => string): Record> } = dual(2, (self: Iterable, f: (a: A) => string): Record> => { 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 }) /** * @since 1.0.0 */ export const unionWith = (isEquivalent: (self: A, that: A) => boolean): { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } => dual(2, (self: Iterable, that: Iterable): Array => { const a = fromIterable(self) const b = fromIterable(that) return isNonEmptyReadonlyArray(a) && isNonEmptyReadonlyArray(b) ? unionNonEmptyWith(isEquivalent)(a, b) : isNonEmptyReadonlyArray(a) ? a : b }) /** * @since 1.0.0 */ export const union: { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } = unionWith(_equivalence) /** * @since 1.0.0 */ export const unionNonEmptyWith = (isEquivalent: (self: A, that: A) => boolean): { (that: NonEmptyReadonlyArray): (self: ReadonlyArray) => NonEmptyArray (that: ReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: ReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray (self: NonEmptyReadonlyArray, that: ReadonlyArray): NonEmptyArray } => { const dedupe = dedupeNonEmptyWith(isEquivalent) return dual( 2, (self: NonEmptyReadonlyArray, that: ReadonlyArray): NonEmptyArray => dedupe(appendAllNonEmpty(self, that)) ) } /** * @since 1.0.0 */ export const unionNonEmpty: { (that: NonEmptyReadonlyArray): (self: ReadonlyArray) => NonEmptyArray (that: ReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: ReadonlyArray, that: NonEmptyReadonlyArray): NonEmptyArray (self: NonEmptyReadonlyArray, that: ReadonlyArray): NonEmptyArray } = unionNonEmptyWith(_equivalence) /** * Creates an `Array` of unique values that are included in all given `Iterable`s using the provided `isEquivalent` function. * The order and references of result values are determined by the first `Iterable`. * * @since 1.0.0 */ export const intersectionWith = (isEquivalent: (self: A, that: A) => boolean): { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } => { const has = containsWith(isEquivalent) return dual( 2, (self: Iterable, that: Iterable): Array => fromIterable(self).filter((a) => has(that, a)) ) } /** * Creates an `Array` of unique values that are included in all given `Iterable`s. * The order and references of result values are determined by the first `Iterable`. * * @since 1.0.0 */ export const intersection: { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } = intersectionWith(_equivalence) /** * Creates a `Array` of values not included in the other given `Iterable` using the provided `isEquivalent` function. * The order and references of result values are determined by the first `Iterable`. * * @since 1.0.0 */ export const differenceWith = (isEquivalent: (self: A, that: A) => boolean): { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } => { const has = containsWith(isEquivalent) return dual( 2, (self: Iterable, that: Iterable): Array => fromIterable(self).filter((a) => !has(that, a)) ) } /** * Creates a `Array` of values not included in the other given `Iterable` using the provided `isEquivalent` function. * The order and references of result values are determined by the first `Iterable`. * * @since 1.0.0 */ export const difference: { (that: Iterable): (self: Iterable) => Array (self: Iterable, that: Iterable): Array } = differenceWith(_equivalence) /** * @category constructors * @since 1.0.0 */ export const empty: () => Array = () => [] /** * Constructs a new `NonEmptyArray` from the specified value. * * @category constructors * @since 1.0.0 */ export const of = (a: A): NonEmptyArray => [a] /** * @category mapping * @since 1.0.0 */ export const map: { (f: (a: A, i: number) => B): (self: ReadonlyArray) => Array (self: ReadonlyArray, f: (a: A, i: number) => B): Array } = dual(2, (self: ReadonlyArray, f: (a: A, i: number) => B): Array => self.map(f)) /** * @category mapping * @since 1.0.0 */ export const mapNonEmpty: { (f: (a: A, i: number) => B): (self: readonly [A, ...Array]) => [B, ...Array] (self: readonly [A, ...Array], f: (a: A, i: number) => B): [B, ...Array] } = map as any /** * @category sequencing * @since 1.0.0 */ export const flatMap: { (f: (a: A, i: number) => ReadonlyArray): (self: ReadonlyArray) => Array (self: ReadonlyArray, f: (a: A, i: number) => ReadonlyArray): Array } = dual( 2, (self: ReadonlyArray, f: (a: A, i: number) => ReadonlyArray): Array => { if (isEmptyReadonlyArray(self)) { return [] } const out: Array = [] for (let i = 0; i < self.length; i++) { out.push(...f(self[i], i)) } return out } ) /** * @category sequencing * @since 1.0.0 */ export const flatMapNonEmpty: { (f: (a: A, i: number) => NonEmptyReadonlyArray): (self: NonEmptyReadonlyArray) => NonEmptyArray (self: NonEmptyReadonlyArray, f: (a: A, i: number) => NonEmptyReadonlyArray): NonEmptyArray } = flatMap as any /** * @category sequencing * @since 1.0.0 */ export const flatten: (self: ReadonlyArray>) => Array = flatMap(identity) /** * @category sequencing * @since 1.0.0 */ export const flattenNonEmpty: ( self: NonEmptyReadonlyArray> ) => NonEmptyArray = flatMapNonEmpty(identity) /** * @category filtering * @since 1.0.0 */ export const filterMap: { (f: (a: A, i: number) => Option): (self: Iterable) => Array (self: Iterable, f: (a: A, i: number) => Option): Array } = dual( 2, (self: Iterable, f: (a: A, i: number) => Option): Array => { const as = fromIterable(self) const out: Array = [] for (let i = 0; i < as.length; i++) { const o = f(as[i], i) if (O.isSome(o)) { out.push(o.value) } } return out } ) /** * Transforms all elements of the `readonlyArray` for as long as the specified function returns some value * * @category filtering * @since 1.0.0 */ export const filterMapWhile: { (f: (a: A) => Option): (self: Iterable) => Array (self: Iterable, f: (a: A) => Option): Array } = dual(2, (self: Iterable, f: (a: A) => Option) => { const out: Array = [] for (const a of self) { const b = f(a) if (O.isSome(b)) { out.push(b.value) } else { break } } return out }) /** * @category filtering * @since 1.0.0 */ export const partitionMap: { (f: (a: A, i: number) => Either): (self: Iterable) => [Array, Array] (self: Iterable, f: (a: A, i: number) => Either): [Array, Array] } = dual( 2, (self: Iterable, f: (a: A, i: number) => Either): [Array, Array] => { const left: Array = [] const right: Array = [] const as = fromIterable(self) for (let i = 0; i < as.length; i++) { const e = f(as[i], i) if (E.isLeft(e)) { left.push(e.left) } else { right.push(e.right) } } return [left, right] } ) /** * @category filtering * @since 1.0.0 */ export const compact: (self: Iterable>) => Array = filterMap(identity) /** * @category filtering * @since 1.0.0 */ export const filter: { ( refinement: (a: A, i: number) => a is B ): (self: Iterable) => Array (predicate: (a: A, i: number) => boolean): (self: Iterable) => Array ( self: Iterable, refinement: (a: A, i: number) => a is B ): Array (self: Iterable, predicate: (a: A, i: number) => boolean): Array } = dual( 2, (self: Iterable, predicate: (a: A, i: number) => boolean): Array => { const as = fromIterable(self) const out: Array = [] for (let i = 0; i < as.length; i++) { if (predicate(as[i], i)) { out.push(as[i]) } } return out } ) /** * @category filtering * @since 1.0.0 */ export const partition: { (refinement: (a: A, i: number) => a is B): ( self: Iterable ) => [Array, Array] ( predicate: (a: A, i: number) => boolean ): (self: Iterable) => [Array, Array] ( self: Iterable, refinement: (a: A, i: number) => a is B ): [Array, Array] ( self: Iterable, predicate: (a: A, i: number) => boolean ): [Array, Array] } = dual( 2, ( self: Iterable, predicate: (a: A, i: number) => boolean ): [Array, Array] => { const left: Array = [] const right: Array = [] const as = fromIterable(self) for (let i = 0; i < as.length; i++) { if (predicate(as[i], i)) { right.push(as[i]) } else { left.push(as[i]) } } return [left, right] } ) /** * @category filtering * @since 1.0.0 */ export const separate: (self: Iterable>) => [Array, Array] = partitionMap( identity ) /** * @category folding * @since 1.0.0 */ export const reduce: { (b: B, f: (b: B, a: A, i: number) => B): (self: Iterable) => B (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B } = dual( 3, (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B => fromIterable(self).reduce((b, a, i) => f(b, a, i), b) ) /** * @category folding * @since 1.0.0 */ export const reduceRight: { (b: B, f: (b: B, a: A, i: number) => B): (self: Iterable) => B (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B } = dual( 3, (self: Iterable, b: B, f: (b: B, a: A, i: number) => B): B => fromIterable(self).reduceRight((b, a, i) => f(b, a, i), b) ) /** * @category lifting * @since 1.0.0 */ export const liftPredicate: { (refinement: Refinement): (c: C) => Array (predicate: Predicate): (b: B) => Array } = (predicate: Predicate) => (b: B) => predicate(b) ? [b] : [] /** * @category lifting * @since 1.0.0 */ export const liftOption = , B>( f: (...a: A) => Option ) => (...a: A): Array => fromOption(f(...a)) /** * @category conversions * @since 1.0.0 */ export const fromNullable = (a: A): Array> => a == null ? empty() : [a as NonNullable] /** * @category lifting * @since 1.0.0 */ export const liftNullable = , B>( f: (...a: A) => B | null | undefined ): (...a: A) => Array> => (...a) => fromNullable(f(...a)) /** * @category combining * @since 1.0.0 */ export const flatMapNullable: { (f: (a: A) => B | null | undefined): (self: ReadonlyArray) => Array> (self: ReadonlyArray, f: (a: A) => B | null | undefined): Array> } = dual( 2, (self: ReadonlyArray, f: (a: A) => B | null | undefined): Array> => isNonEmptyReadonlyArray(self) ? fromNullable(f(headNonEmpty(self))) : empty() ) /** * @category lifting * @since 1.0.0 */ export const liftEither = , E, B>( f: (...a: A) => Either ) => (...a: A): Array => { const e = f(...a) return E.isLeft(e) ? [] : [e.right] } /** * Check if a predicate holds true for every `ReadonlyArray` element. * * @category elements * @since 1.0.0 */ export const every: { (refinement: Refinement): (self: ReadonlyArray) => self is ReadonlyArray (predicate: Predicate): (self: ReadonlyArray) => boolean (self: ReadonlyArray, refinement: Refinement): self is ReadonlyArray (self: ReadonlyArray, predicate: Predicate): boolean } = dual( 2, (self: ReadonlyArray, refinement: Refinement): self is ReadonlyArray => self.every(refinement) ) /** * Check if a predicate holds true for some `ReadonlyArray` element. * * @category elements * @since 1.0.0 */ export const some: { (predicate: Predicate): (self: ReadonlyArray) => self is NonEmptyReadonlyArray (self: ReadonlyArray, predicate: Predicate): self is NonEmptyReadonlyArray } = dual( 2, (self: ReadonlyArray, predicate: Predicate): self is NonEmptyReadonlyArray => self.some(predicate) ) /** * @since 1.0.0 */ export const extend: { (f: (as: ReadonlyArray) => B): (self: ReadonlyArray) => Array (self: ReadonlyArray, f: (as: ReadonlyArray) => B): Array } = dual( 2, (self: ReadonlyArray, f: (as: ReadonlyArray) => B): Array => self.map((_, i, as) => f(as.slice(i))) ) /** * @since 1.0.0 */ export const min: { (O: Order.Order): (self: NonEmptyReadonlyArray) => A (self: NonEmptyReadonlyArray, O: Order.Order): A } = dual(2, (self: NonEmptyReadonlyArray, O: Order.Order): A => self.reduce(Order.min(O))) /** * @since 1.0.0 */ export const max: { (O: Order.Order): (self: NonEmptyReadonlyArray) => A (self: NonEmptyReadonlyArray, O: Order.Order): A } = dual(2, (self: NonEmptyReadonlyArray, O: Order.Order): A => self.reduce(Order.max(O))) /** * @category constructors * @since 1.0.0 */ export const unfold = (b: B, f: (b: B) => Option): Array => { const out: Array = [] let next: B = b let o: Option while (O.isSome(o = f(next))) { const [a, b] = o.value out.push(a) next = b } return out } /** * This function creates and returns a new `Order` for an array of values based on a given `Order` for the elements of the array. * The returned `Order` compares two arrays by applying the given `Order` to each element in the arrays. * If all elements are equal, the arrays are then compared based on their length. * It is useful when you need to compare two arrays of the same type and you have a specific way of comparing each element of the array. * * @category instances * @since 1.0.0 */ export const getOrder: (O: Order.Order) => Order.Order> = Order.array /** * @category instances * @since 1.0.0 */ export const getEquivalence: ( isEquivalent: Equivalence.Equivalence ) => Equivalence.Equivalence> = Equivalence.array /** * Iterate over the `Iterable` applying `f`. * * @since 1.0.0 */ export const forEach: { (f: (a: A, i: number) => void): (self: Iterable) => void (self: Iterable, f: (a: A, i: number) => void): void } = dual(2, (self: Iterable, f: (a: A, i: number) => void): void => fromIterable(self).forEach((a, i) => f(a, i))) /** * Remove duplicates from am `Iterable` using the provided `isEquivalent` function, keeping the first occurrence of an element. * * @since 1.0.0 */ export const dedupeWith: { (isEquivalent: (self: A, that: A) => boolean): (self: Iterable) => Array (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Array } = dual( 2, (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Array => { const input = fromIterable(self) return isNonEmptyReadonlyArray(input) ? dedupeNonEmptyWith(isEquivalent)(input) : [] } ) /** * Remove duplicates from am `Iterable`, keeping the first occurrence of an element. * * @since 1.0.0 */ export const dedupe: (self: Iterable) => Array = dedupeWith(Equal.equivalence()) /** * Deduplicates adjacent elements that are identical using the provided `isEquivalent` function. * * @since 1.0.0 */ export const dedupeAdjacentWith: { (isEquivalent: (self: A, that: A) => boolean): (self: Iterable) => Array (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Array } = dual(2, (self: Iterable, isEquivalent: (self: A, that: A) => boolean): Array => { const out: Array = [] let lastA: O.Option = O.none() for (const a of self) { if (O.isNone(lastA) || !isEquivalent(a, lastA.value)) { out.push(a) lastA = O.some(a) } } return out }) /** * Deduplicates adjacent elements that are identical. * * @since 1.0.0 */ export const dedupeAdjacent: (self: Iterable) => Array = dedupeAdjacentWith(Equal.equivalence()) /** * Joins the elements together with "sep" in the middle. * * @since 1.0.0 * @category folding */ export const join: { (sep: string): (self: Iterable) => string (self: Iterable, sep: string): string } = dual(2, (self: Iterable, sep: string): string => fromIterable(self).join(sep)) /** * Statefully maps over the chunk, producing new elements of type `B`. * * @since 1.0.0 * @category folding */ export const mapAccum: { (s: S, f: (s: S, a: A) => readonly [S, B]): (self: Iterable) => [S, Array] (self: Iterable, s: S, f: (s: S, a: A) => readonly [S, B]): [S, Array] } = dual(3, (self: Iterable, s: S, f: (s: S, a: A) => [S, B]) => { let s1 = s const out: Array = [] for (const a of self) { const r = f(s1, a) s1 = r[0] out.push(r[1]) } return [s1, out] }) /** * Zips this chunk crosswise with the specified chunk using the specified combiner. * * @since 1.0.0 * @category elements */ export const cartesianWith: { (that: ReadonlyArray, f: (a: A, b: B) => C): (self: ReadonlyArray) => Array (self: ReadonlyArray, that: ReadonlyArray, f: (a: A, b: B) => C): Array } = dual( 3, (self: ReadonlyArray, that: ReadonlyArray, f: (a: A, b: B) => C): Array => flatMap(self, (a) => map(that, (b) => f(a, b))) ) /** * Zips this chunk crosswise with the specified chunk. * * @since 1.0.0 * @category elements */ export const cartesian: { (that: ReadonlyArray): (self: ReadonlyArray) => Array<[A, B]> (self: ReadonlyArray, that: ReadonlyArray): Array<[A, B]> } = dual( 2, (self: ReadonlyArray, that: ReadonlyArray): Array<[A, B]> => cartesianWith(self, that, (a, b) => [a, b]) )