Iiris is an experimental utility library, designed to make it easier to manipulate built-in JavaScript data types like arrays, objects and strings in a functional manner. It is heavily inspired by projects like Ramda and Lodash.
- No mutation of input data.
- Automatically curried, data-last API.
- Performance on par with native JavaScript methods.
- Good out-of-the-box TypeScript typings.
- Small footprint (4 kB gzipped) and excellent tree-shaking support.
- Support only native JavaScript data types.
- Target reasonably current JavaScript environments (Node 10+)
Iiris is still alpha-quality software, so bugs and changes to the API should be expected.
If you've tried Iiris and something doesn't seem to be working as expected, let me know!
- Installation
- Getting Started
- Why Iiris?
- API Reference
Run either
$ npm install iiris
or
$ yarn add iiris
depending on your favourite package manager.
import * as I from 'iiris'
By convention, Iiris is imported to a single-letter variable I
. Here's a small showcase of its main features:
// Problem: If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9.
// The sum of these multiples is 23.
//
// Find the sum of all the multiples of 3 or 5 below 1000.
//
// See https://projecteuler.net/problem=1 for more information
const sumOfMultiples = I.pipe(
I.range(1, 1000),
I.filter((n) => n % 3 === 0 || n % 5 === 0),
I.sum
) // => 233168
Iiris is heavily inspired by libraries like Ramda and Lodash. However, there are a few things that make it different:
Compared to lodash:
- Each function is automatically curried and input data is always the last argument.
- Input data is never mutated.
- Chaining is achieved with function composition instead of special constructs like
_.chain
. - Iiris doesn't support any kind of iteratee shorthands.
Compared to Ramda:
- Much better TypeScript support. Typically, you don't have to add any extra type annotations when using Iiris, even when writing code in point-free style.
- Iiris functions are less polymorphic. For example,
I.map
operates only on arrays, whileR.map
supports arrays, objects and arbitrary fantasy-land functors. TypeScript doesn't have native support for higher-kinded types (although some people have tried to work around that), so I made an intentional decision to limit the polymorphism of Iiris functions. This makes code less general but dramatically improves the TypeScript experience and makes tree-shaking more effective. - No support for placeholders. Placeholders add some overhead to each curried function call and make writing TypeScript typings much harder.
- A bigger focus on performance.
Compared to both:
- Iiris requires a fairly modern JavaScript engine (Node 10+) to run.
Note that we display only the fully curried type signature for all curried functions. Unless otherwise specified, each function is curried and you may pass the desired number of arguments, depending on the context.
For example, I.add
has an arity of 2, so the following are equivalent.
I.add(x, y) === I.add(x)(y)
I.equalsBy
has an arity of 3, so all the following are equivalent.
I.equalsBy(fn, a, b)
I.equalsBy(fn, a)(b)
I.equalsBy(fn)(a, b)
I.equalsBy(fn)(a)(b)
Many of the type signatures are also simplified. As an example, we don't show the
readonly
modifier for each array argument.
<T>(value: T) => (array: T[]) => T[]
Append a new element to the end of an array.
Example:
I.append(4, [1, 2, 3])
// => [1, 2, 3, 4]
<T>(array: T[]) => (other: T[]) => T[]
Concatenate two arrays together.
Example:
I.concat([1, 2, 3], [4, 5, 6])
// => [1, 2, 3, 4, 5, 6]
<T>(fn: (value: T) => void) => (array: T[]) => T[]
Apply fn
to each element of the array
and return the array
.
Example:
I.forEach(console.log, ['h', 'i', '!'])
h
i
!
// => ['h', 'i', '!']
See also: forEachWithIndex
<T>(fn: (index: number, value: T) => void) => (array: T[]) => T[]
Like forEach, but fn
also receives the element index as the first
argument.
Example:
I.forEachWithIndex(console.log, ['h', 'i', '!'])
0 h
1 i
2 !
// => ['h', 'i', '!']
See also: forEach
<T>(array: T[]) => T | undefined
Return the first element of the array
or undefined
.
Example:
I.head([1, 2, 3])
// => 1
I.head([])
// => undefined
<T>(array: T[]) => T[]
Return all elements of the array
except the last.
Example:
I.init([1, 2, 3])
// => [1, 2]
I.init([])
// => []
<T>(array: T[]) => boolean
Check if array
is empty.
Example:
I.isEmpty([1, 2, 3])
// => false
I.isEmpty([])
// => true
See also: length
<T>(array: T[]) => T | undefined
Return the last element of the array
or undefined
.
Example:
I.last([1, 2, 3])
// => 3
I.last([])
// => undefined
<T>(array: T[]) => number
Return the length of an array
.
Example:
I.length([1, 2, 3])
// => 3
I.length([])
// => 0
See also: isEmpty
(index: number) => <T>(fn: (value: T) => T) => (array: T[]) => T[]
Returns a copy of array
where the nth element has been replaced by applying
fn
to its current value.
- If
index
is not withinarray
bounds, thearray
is returned unchanged. - Removes the element if
fn
returnsundefined
.
Example:
I.modifyNth(0, I.inc, [1, 2, 3])
// => [2, 2, 3]
I.modifyNth(-1, I.inc, [1, 2, 3])
// => [1, 2, 4]
I.modifyNth(0, I.noop, [1, 2, 3])
// => [2, 3]
I.modifyNth(999, I.inc, [1, 2, 3])
// => [1, 2, 3]
(index: number) => <T>(array: T[]) => T | undefined
Return the nth element from array
or undefined
.
Example:
I.nth(0, [1, 2, 3])
// => 1
I.nth(0, [])
// => undefined
(index: number) => <T>(value: T) => (array: T[]) => boolean
Check if the nth element of array
equals value
, using equals for
determining equality.
Example:
I.nthEquals(0, 'a', ['a', 'b', 'c'])
// => true
See also: nthSatisfies
<T>(defaultValue: T) => (index: number) => (array: T[]) => T
Like nth, but if the resolved value is undefined
, defaultValue
is
returned instead.
Example:
I.nthOr(999, 0, [1, 2, 3])
// => 1
I.nthOr(999, 0, [])
// => 999
I.nthOr(999, 0, [undefined])
// => 999
(index: number) => <T>(predicate: (value: T) => boolean) => (array: T[]) => boolean
Check if the nth element of array
satisfies the predicate
.
Example:
I.nthSatisfies(0, I.gt(0), [1, 2, 3])
// => true
See also: nthSatisfies
<T>(value: T) => (array: T[]) => T[]
Prepend a new element to the beginning of an array.
Example:
I.prepend(0, [1, 2, 3])
// => [0, 1, 2, 3]
(index: number) => <T>(array: T[]) => T[]
Returns a copy of array
without the nth element.
- If
index
is not within thearray
bounds, thearray
is returned unchanged.
Example:
I.removeNth(0, [1, 2, 3])
// => [2, 3]
I.removeNth(-1, [1, 2, 3])
// => [1, 2]
I.removeNth(999, [1, 2, 3])
// => [1, 2, 3]
(index: number) => <T>(value: undefined | T) => (array: T[]) => T[]
Returns a copy of array
where nth element has been replaced with value
.
- If
index
is not within thearray
bounds, thearray
is returned unchanged. - Removes the element if
value
isundefined
.
Example:
I.setNth(0, 999, [1, 2, 3])
// => [999, 2, 3]
I.setNth(-1, 999, [1, 2, 3])
// => [1, 2, 999]
I.setNth(999, 999, [1, 2, 3])
// => [1, 2, 3]
I.setNth(0, undefined, [1, 2, 3])
// => [2, 3]
See also: modifyNth, removeNth
<T>(array: T[]) => T[]
Return all elements of the array
except the first.
Example:
I.tail([1, 2, 3])
// => [2, 3]
I.tail([])
// => []
<T, U>(fn: (value: T) => U[]) => (array: T[]) => U[]
Return an array containing the results of applying fn
to each element in
the original array
and then flattening the result by one level.
Example:
I.flatMap((n) => [n, n], [1, 2, 3])
// => [1, 1, 2, 2, 3, 3]
<D extends number>(depth: D) => <T extends unknown[]>(array: T) => Array<FlatArray<T, D>>
Flatten a nested array
by n
levels.
Example:
I.flatten(1, [1, [2, [3]]])
// => [1, 2, [3]]
I.flatten(2, [1, [2, [3]]])
// => [1, 2, 3]
See also: flatMap
<T>(separator: T) => (array: T[]) => T[]
Return a copy of array
with separator
inserted between each element.
Example:
I.intersperse(',', ['a', 'b', 'c'])
// => ['a', ',', 'b', ',', 'c']
I.intersperse(',', [])
// => []
See also: join
(separator: string) => <T>(array: T[]) => string
Convert the array
to a string, inserting the separator
between each
element.
Example:
I.join(', ', [1, 2, 3])
// => '1, 2, 3'
See also: split, intersperse
<T, U>(fn: (value: T) => U) => (array: T[]) => U[]
Return an array containing the results of applying fn
to each element in
the original array
.
Example:
I.map(I.inc, [1, 2, 3])
// => [2, 3, 4]
See also: mapWithIndex, mapMaybe, flatMap
<T, U>(fn: (value: T) => U | undefined) => (array: T[]) => U[]
Return an array containing the results of applying fn
to each element in
the original array
, discarding any undefined
values.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: undefined },
{ name: 'Carol', age: 20 }
]
I.mapMaybe(I.prop('age'), users)
// => [10, 20]
See also: map
<T, U>(fn: (index: number, value: T) => U) => (array: T[]) => U[]
Like map, but fn
also receives the element index as the first
argument.
Example:
I.mapWithIndex((i, c) => `${i}-${c}`, ['a', 'b', 'c'])
// => ['0-a', '1-b', '2-c']
See also: map
<T>(array: T[]) => T[]
Reverse an array
.
Example:
I.reverse([1, 2, 3])
// => [3, 2, 1]
<T extends Ordered>(array: T[]) => T | undefined
Return the largest element of an array
or undefined
.
Example:
I.maximum([1, 2, 3])
// => 3
I.maximum([])
// => undefined
<T, U extends Ordered>(fn: (value: T) => U) => (array: T[]) => T | undefined
Like maximum, but apply fn
to each value before determining
their ordering.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: 20 },
{ name: 'Carol', age: 30 },
]
I.maximumBy((u) => u.age, users)
// => { name: 'Carol', age: 30 }
<T extends Ordered>(array: T[]) => T | undefined
Return the smallest element of array
or undefined
.
Example:
I.minimum([1, 2, 3])
// => 1
I.minimum([])
// => undefined
<T, U extends Ordered>(fn: (value: T) => U) => (array: T[]) => T | undefined
Like minimum, but fn
is applied to each value before determining
their ordering.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: 20 },
{ name: 'Carol', age: 30 },
]
I.minimumBy((u) => u.age, users)
// => { name: 'Alice', age: 10 }
<T, R>(reducer: (accumulator: R, value: T) => R) => (initial: R) => (array: T[]) => R
Left-associative fold.
Combine the elements of an array
in to a single value by calling reducer
with the accumulated value so far and the current element. The first call to
reducer
receives initial
as the accumulator.
If the array is empty, initial
is returned.
Example:
I.reduce((sum, n) => sum + n, 1, [2, 3, 4]) // equal to ((1 + 2) + 3) + 4
// => 10
See also: reduceRight
<T, R>(reducer: (value: T, accumulator: R) => R) => (initial: R) => (array: T[]) => R
Right-associative fold.
Combine the elements of an array
in to a single value by calling reducer
with the current element and the accumulated value so far. The first call to
reducer
receives initial
as the accumulator.
If the array is empty, initial
is returned.
Example:
I.reduceRight((n, sum) => n + sum, 4, [1, 2, 3]) // equal to 1 + (2 + (3 + 4))
// => 10
See also: reduce
(numbers: number[]) => number
Sum an array
of numbers together. Returns 0
if the array is empty.
Uses the Kahan summation algorithm for minimizing numerical error.
Example:
const numbers = I.repeat(0.1, 10)
// => [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
I.sum(numbers)
// => 1
numbers.reduce((sum, n) => sum + n, 0)
// => 0.9999999999999999
See also: sumBy
<T>(fn: (value: T) => number) => (array: T[]) => number
Like sum, but each element of the array
is converted to a number
by applying fn
.
Example:
I.sumBy(I.prop('age'), [{ name: 'Alice', age: 10 }, { name: 'Bob', age: 20 }])
// => 30
See also: sum
<T>(predicate: (value: T) => boolean) => (array: T[]) => number
Count the number of elements in the array
the satisfy the predicate
.
Example:
I.count((n) => n > 1, [1, 2, 3])
// => 2
See also: filter
<T>(predicate: (value: T) => boolean) => (array: T[]) => boolean
Check if every element in the array
satisfies the predicate
.
Example:
I.every((n) => n < 10, [1, 2, 3])
// => true
I.every((n) => n < 3, [1, 2, 3])
// => false
<T>(predicate: (value: T) => boolean) => (array: T[]) => T[]
<T, U>(guard: (value: T) => value is U) => (array: T[]) => U[]
Return the elements of the array
that satisfy the predicate
.
Example:
I.filter((n) => n > 1, [1, 2, 3])
// => [2, 3]
See also: filterWithIndex, count, partition
<T>(predicate: (index: number, value: T) => boolean) => (array: T[]) => T[]
Like filter, but predicate
also receives the element index as the
first argument.
Example:
I.filterWithIndex((i, n) => i + n === 3, [1, 2, 3])
// => [2]
See also: filter
<T>(predicate: (value: T) => boolean) => (array: T[]) => T | undefined
<T, U>(guard: (value: T) => value is U) => (array: T[]) => U | undefined
Find the first element in the array
that satisfies the predicate
.
Returns undefined
if none of the elements match.
Example:
I.find((c) => c !== 'a', ['a', 'b', 'c'])
// => 'b'
I.find((c) => c === 'x', ['a', 'b', 'c'])
// => undefined
<T>(predicate: (value: T) => boolean) => (array: T[]) => number
Find the index of the first element in the array
that satisfies the
predicate
.
Returns -1
if none of the elements satisfy the predicate.
Example:
I.findIndex((c) => c !== 'a', ['a', 'b', 'c'])
// => 1
I.findIndex((c) => c === 'x', ['a', 'b', 'c'])
// => -1
See also: findLastIndex, find
<T>(predicate: (value: T) => boolean) => (array: T[]) => T | undefined
<T, U>(guard: (value: T) => value is U) => (array: T[]) => U | undefined
Find the last element in the array
that satisfies the predicate
.
Returns undefined
if none of the elements match.
Example:
I.findLast((c) => c !== 'a', ['a', 'b', 'c'])
// => 'c'
I.findLast((c) => c === 'x', ['a', 'b', 'c'])
// => undefined
See also: find, findLastIndex
<T>(predicate: (value: T) => boolean) => (array: T[]) => number
Find the index of the last element in the array
that satisfies the
predicate
.
Returns -1
if none of the elements match.
Example:
I.findLastIndex((c) => c !== 'a', ['a', 'b', 'c'])
// => 2
I.findLastIndex((c) => c === 'x', ['a', 'b', 'c'])
// => -1
<T>(predicate: (value: T) => boolean) => (array: T[]) => boolean
Check if none of the elements in the array
satisfy the predicate
.
Example:
I.none((n) => n > 5, [1, 2, 3])
// => true
I.none((n) => n > 5, [1, 2, 3])
// => false
<T>(predicate: (value: T) => boolean) => (array: T[]) => [T[], T[]]
<T, U>(guard: (value: T) => value is U) => (array: T[]) => [U[], Array<Exclude<T, U>>]
Partition the array
into two arrays, the first containing the elements
that satisfy the predicate
and the second containing the elements that do
not.
Example:
const [evens, odds] = I.partition((n) => n % 2 === 0, [1, 2, 3])
// => [[2], [1, 3]]
See also: filter
<T>(predicate: (value: T) => boolean) => (array: T[]) => boolean
Check if some elements in the array
satisfies the predicate
.
Example:
I.some((n) => n > 2, [1, 2, 3])
// true
I.some((n) => n > 5, [1, 2, 3])
// false
<T>(value: T) => (array: T[]) => boolean
Check if the array
includes the specified value
, using equals for
determining equality.
Example:
I.includes(1, [1, 2, 3])
// => true
I.includes(0, [1, 2, 3])
// => false
<T>(value: T) => (array: T[]) => number
Return the index of the first element equaling value
, using equals
for determining equality. Returns -1
if no match can be found.
Example:
I.indexOf('b', ['a', 'b', 'c', 'a', 'b', 'c'])
// => 1
I.indexOf('x', ['a', 'b', 'c', 'a', 'b', 'c'])
// => -1
See also: lastIndexOf, includes
<T>(value: T) => (array: T[]) => number
Return the index of the last element equaling value
, using equals
for determining equality. Returns -1
if no match can be found.
Example:
I.lastIndexOf('b', ['a', 'b', 'c', 'a', 'b', 'c'])
// => 4
I.lastIndexOf('x', ['a', 'b', 'c', 'a', 'b', 'c'])
// => -1
<T, K extends string>(keyFn: (value: T) => K) => (array: T[]) => Record<K, number>
Apply keyFn
to each element in the array
and return an object of counts
by key.
Example:
const users = [
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Alice' }
]
I.countBy(I.prop('name'), users)
// => { Alice: 2, Bob: 1 }
See also: groupBy
<T, K extends string>(keyFn: (value: T) => K) => (array: T[]) => Record<K, T[]>
Partition the array
into an object of arrays according to keyFn
.
Example:
const users = [
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Alice' },
]
I.groupBy(I.prop('name'), users)
// => { Alice: [{ name: 'Alice' }, { name: 'Alice' }], Bob: [{ name: 'Bob' }] }
See also: indexBy, countBy, groupMap, groupMapReduce
<T, U>(mapFn: (value: T) => U) => <K extends string>(keyFn: (value: T) => K) => (array: T[]) => Record<K, U[]>
Like groupBy, but also apply mapFn
to each element before adding
it to the corresponding array.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: 20 },
{ name: 'Alice', age: 30 }
]
const agesByName = I.groupMap(I.prop('age'), I.prop('name'), users)
// => { Alice: [10, 30], Bob: [20] }
See also: groupBy, groupMapReduce
<U>(reducer: (accumulator: U, value: U) => U) => <T>(mapFn: (value: T) => U) => <K extends string>(keyFn: (value: T) => K) => (array: T[]) => Record<K, U>
Like groupMap, but instead of returning an object of arrays, combine
elements mapping to the same key with reducer
.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: 20 },
{ name: 'Alice', age: 30 }
]
const sumOfAgesByName = I.groupMapReduce(I.add, I.prop('age'), I.prop('name'), users)
// => { Alice: 40, Bob: 20 }
<T, K extends string>(keyFn: (value: T) => K) => (array: T[]) => Record<K, T>
Apply keyFn
to each element in the array
and return an object of
elements indexed by each key.
If multiple elements map to the same key, the last one is selected.
Example:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Carol' }
]
I.indexBy(I.prop('id'), users)
// => { '1': { id: 1, name: 'Carol' }, '2': { id: 2, name: 'Bob' } }
See also: groupBy
<T>(value: T) => [T]
Create a singleton array containing value
Example:
I.of(1)
// => [1]
See also: pair
<T>(first: T) => <U>(second: U) => [T, U]
Create two element array containing first
and second
.
Example:
I.pair(1, 2)
// => [1, 2]
See also: of
(start: number) => (end: number) => number[]
Create an array of numbers between start
(inclusive) and end
(exclusive).
Example:
I.range(0, 10)
// => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// I.range(0, 0)
// => []
<T>(value: T) => (n: number) => T[]
Repeat the given value
n
times.
Example:
I.repeat('a', 5)
// => ['a', 'a', 'a', 'a', 'a']
<T>(fn: (index: number) => T) => (n: number) => T[]
Create an array of length n
by applying fn
to the index of each element.
Example:
I.times((n) => n * 10, 3)
// => [0, 10, 20]
(n: number) => <T>(array: T[]) => T[]
Drop the first n
elements of an array
.
Example:
I.drop(1, [1, 2, 3])
// => [2, 3]
I.drop(2, [1, 2, 3])
// => [3]
(n: number) => <T>(array: T[]) => T[]
Drop the last n
elements of an array
.
Example:
I.dropLast(1, [1, 2, 3])
// => [1, 2]
I.dropLast(2, [1, 2, 3])
// => [1]
<T>(predicate: (value: T) => boolean) => (array: T[]) => T[]
Drop elements from the end of an array
while predicate
is satisfied.
Example:
I.dropLastWhile((n) => n > 1, [1, 2, 3])
// => [1]
See also: dropWhile, takeLastWhile
<T>(predicate: (value: T) => boolean) => (array: T[]) => T[]
Drop elements from the beginning of an array
while predicate
is
satisfied.
Example:
I.dropWhile((n) => n === 1, [1, 2, 3])
// => [2, 3]
See also: dropLastWhile, takeWhile
(start: number) => (end: number) => <T>(array: T[]) => T[]
Create a copy of array
containing the elements from start
(inclusive)
to end
(exclusive).
Example:
I.slice(0, 2, [1, 2, 3])
// => [1, 2]
I.slice(1, 2, [1, 2, 3])
// => [2]
(n: number) => <T>(array: T[]) => T[]
Take the first n
elements of an array
.
Example:
I.take(2, [1, 2, 3])
// => [1, 2]
<T>(n: number) => (array: T[]) => T[]
Take the last n
elements of an array
.
Example:
I.takeLast(2, [1, 2, 3])
// => [2, 3]
<T>(predicate: (value: T) => boolean) => (array: T[]) => T[]
Take elements from the end of an array
while predicate
is satisfied.
Example:
I.takeLastWhile((n) => n >= 2, [1, 2, 3])
// => [2, 3]
See also: dropLastWhile, takeWhile
<T>(predicate: (value: T) => boolean) => (array: T[]) => T[]
Take elements from the beginning of an array
while predicate
is
satisfied.
Example:
I.takeWhile((n) => n <= 2, [1, 2, 3])
// => [1, 2]
See also: dropWhile, takeLastWhile
<T, U extends Ordered>(fn: (value: T) => U) => (first: T, second: T) => number
Given a fn
that maps a value
to an Ordered value, create an
ascending comparator function.
Note: The returned function is not curried.
Example:
I.sort(I.ascend(I.prop('age')), [{ name: 'Bob' }, { name: 'Alice' }])
// => [{ name: 'Alice' }, { name: 'Bob' }]
See also: descend, sort, sortWith
<T, U extends Ordered>(fn: (value: T) => U) => (first: T, second: T) => number
Given a fn
that maps a value
to an Ordered value, create a
descending comparator function.
Note: The returned function is not curried.
Example:
I.sort(I.descend(I.prop('name')), [{ name: 'Alice' }, { name: 'Bob' }])
// => [{ name: 'Bob' }, { name: 'Alice' }]
See also: ascend, sort, sortWith
<T>(comparator: (first: T, second: T) => number) => (array: T[]) => T[]
Sort an array
according to the comparator function.
Example:
I.sort((a, b) => a - b, [3, 2, 1])
// => [1, 2, 3]
See also: sortBy, sortWith, ascend, descend
<T, U extends Ordered>(fn: (value: T) => U) => (array: T[]) => T[]
Sort an array
into ascending order by mapping each element of the array
with fn
.
Example:
const users = [
{ name: 'Bob', age: 10 },
{ name: 'Alice', age: 20 }
]
I.sortBy(I.prop('name'), users)
// => [{ name: 'Alice', age: 20 }, { name: 'Bob', age: 10 }]
I.sortBy(I.prop('age'), users)
// => [{ name: 'Bob', age: 10 }, { name: 'Alice', age: 20 }]
<T>(comparators: Array<(first: T, second: T) => number>) => (array: T[]) => T[]
Sort an array
according to an array of comparator functions.
The comparators are tried in order until an ordering has been found.
Example:
const users = [
{ name: 'Alice', age: 10 },
{ name: 'Bob', age: 20 },
{ name: 'Alice', age: 20 },
]
I.sortWith([I.descend(I.prop('age')), I.ascend(I.prop('name'))], users)
// => [{ name: 'Alice', age: 20 }, { name: 'Bob', age: 20 }, { name: 'Alice', age: 10 }]
See also: sort, sortBy, ascend, descend
<T>(first: T[]) => <U>(second: U[]) => Array<[T, U]>
Combine the corresponding elements of two arrays into an array of pairs.
If one of the arrays is longer than the other, the extra elements are ignored.
Example:
I.zip(['a', 'b', 'c'], [1, 2, 3])
// => [['a', 1], ['b', 2], ['c', 3]]
<K extends string>(keys: K[]) => <T>(values: T[]) => Record<K, T>
Combine an array of keys
and values
into an object.
If one of the arrays is longer than the other, its extra elements are ignored.
Example:
I.zipObject(['a', 'b', 'c'], [1, 2, 3])
// => { a: 1, b: 2, c: 3 }
See also: zip, fromEntries
<T, U, R>(fn: (value: T, other: U) => R) => (first: T[]) => (second: U[]) => R[]
Like zip, but the elements are combined with fn
instead of
constructing a pair.
Example:
I.zipWith(I.add, [1, 2, 3], [4, 5, 6])
// => [5, 7, 9]
See also: zip
<T>(first: T[], second: T[]) => T[]
Calculate the set
difference
between the first
array and the second
array, using equals for
determining equality.
Will not remove duplicates from the first
array.
Example:
I.difference([1, 2, 3], [2, 3, 4])
// => [1]
See also: differenceWith, union, intersection
<T>(equals: (value: T, other: T) => boolean) => (array: T[]) => (other: T[]) => T[]
Like difference, but using a custom equality function.
Example:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
]
const otherUsers = [
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
{ id: 4, name: 'Dan' }
]
I.differenceWith((a, b) => a.id === b.id, users, otherUsers)
// => [ { id: 1, name: 'Alice' } ]
See also: difference, unionWith, intersectionWith
<T>(first: T[]) => (second: T[]) => T[]
Calculate the set
intersection
between the first
array and the second
array, using equals for
determining equality.
Will not remove duplicates from the first array.
Example:
I.intersection([1, 2, 3], [2, 3, 4])
// => [2, 3]
See also: intersectionWith, union, difference
<T>(equals: (value: T, other: T) => boolean) => (array: T[]) => (other: T[]) => T[]
Like intersection, but using a custom equality function.
Example:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
]
const otherUsers = [
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
{ id: 4, name: 'Dan' }
]
I.intersectionWith((a, b) => a.id === b.id, users, otherUsers)
// => [ { id: 2, name: 'Bob' }, { id: 3, name: 'Carol' } ]
See also: intersection, unionWith, differenceWith
<T>(first: T[]) => (second: T[]) => T[]
Calculate the set union
between the first
array and the second
array, using equals for
determining equality.
Will not remove duplicates from the first array.
Example:
I.union([1, 2, 3], [2, 3, 4])
// => [1, 2, 3, 4]
See also: unionWith, intersection, difference
<T>(equals: (value: T, other: T) => boolean) => (array: T[]) => (other: T[]) => T[]
Like union, but using a custom equality function.
Example:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
]
const otherUsers = [
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Carol' },
{ id: 4, name: 'Dan' }
]
I.unionWith((a, b) => a.id === b.id, users, otherUsers)
// => [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Carol' }, { id: 4, name: 'Dan' } ]
See also: union, intersectionWith, differenceWith
<T>(array: T[]) => T[]
Remove duplicate values from array
, using equals for determining
equality.
Example:
I.uniq([1, 2, 3, 1, 2, 3])
// => [1, 2, 3]
See also: uniqWith
<T>(equals: (value: T, other: T) => boolean) => (array: T[]) => T[]
Like uniq, but using a custom equality function.
Example:
const users = [
{ id: 1, name: 'Alice' },
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
]
I.uniqWith((a, b) => a.id === b.id, users)
// => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
See also: uniq
<T extends object, K extends string>(object: T) => Array<[K, T[K]]>
Return an array of the own enumerable property key-value pairs of object
Example:
I.entries({ a: 1, b: 2, c: 3 })
// => [['a', 1], ['b', 2], ['c', 3]]
See also: fromEntries, keys, values
<K extends string, T>(entries: Array<[K, T]>) => Record<K, T>
Create an object from an array of [key, value]
pairs.
Example:
I.fromEntries([['a', 1], ['b', 2], ['c', 3]])
// => { a: 1, b: 2, c: 3 }
See also: entries
<K extends string>(key: K) => (object: unknown) => object is Record<K, unknown>
Check if key
is an own property of object
.
Example:
I.has('a', { a: 1 })
// => true
I.has('toString', { a: 1 })
// => false
<T extends object>(object: T) => Array<keyof T & string>
Return an array of the own enumerable property keys of object
.
Example:
I.keys({ a: 1, b: 2, c: 3 })
// => ['a', 'b', 'c']
<K1 extends string, K2 extends string>(fn: (value: K1) => K2) => <V>(object: Record<K1, V>) => Record<K2, V>
Return an object containing the results of applying fn
to each key of
the original object
.
If multiple keys map to the same new key, the latest value is selected.
Example:
I.mapKeys((k) => k.toUpperCase(), { a: 1, b: 2, c: 3 })
// => { A: 1, B: 2, C: 3 }
<V1, V2>(fn: (value: V1) => V2) => <K extends string>(object: Record<K, V1>) => Record<K, V2>
Return an object containing the results of applying fn
to each value of
the original object
.
Example:
I.mapValues(I.inc, { a: 1, b: 2, c: 3 })
// => { a: 2, b: 3, c: 4 }
<T extends object>(first: T) => <U extends object>(second: U) => T & U
Copy the own enumerable properties of two objects, preferring the values from
second
in case of duplicate keys.
Example:
I.merge({ a: 1, b: 1 }, { b: 2, c: 2 })
// => { a: 1, b: 2, c: 2 }
<K extends string>(key: K) => <V>(fn: (value: V) => V) => <T extends HasKey<K, V>>(object: T) => T
Return a copy of object
where the property key
has replaced by applying
fn
to its current value.
- If
key
is not an own property ofobject
, theobject
is returned unchanged. - If
fn
returnsundefined
, the property is removed.
Example:
I.modifyProp('a', (n) => n + 1, { a: 1, b: 2, c: 3 })
// => { a: 2, b: 2, c: 3 }
I.modifyProp('a', () => undefined, { a: 1, b: 2, c: 3 })
// => { b: 2, c: 3 }
I.modifyProp('d', () => 4, { a: 1, b: 2, c: 3 })
// => { a: 1, b: 2, c: 3, d: 4 }
See also: setProp, removeProp
<K extends string>(keys: K[]) => <T extends HasKey<K>>(object: T) => Omit<T, Extract<keyof T, K>>
Return a copy of object
without the specified keys
.
Example:
I.omit(['a', 'b'], { a: 1, b: 2, c: 3 })
// => { c: 3 }
See also: pick
<K extends string>(keys: K[]) => <T extends HasKey<K>>(object: T) => Pick<T, Extract<keyof T, K>>
Return a copy of object
with only the specified keys
.
Example:
I.pick(['a', 'b'], { a: 1, b: 2, c: 3 })
// => { a: 1, b: 2 }
See also: omit
<K extends string>(key: K) => <T extends HasKey<K>>(object: T) => T[K]
Retrieves the property key
from object
or undefined
.
Example:
I.prop('a', { a: 1, b: 2, c: 3 })
// => 1
I.prop('a', {})
// => undefined
<K extends string>(key: K) => <V>(value: V) => <T extends HasKey<K, V>>(object: T) => boolean
Check if property key
of object
equals value
, using equals for
determining equality.
Example:
const users = [
{ name: 'Alice' },
{ name: 'Bob' },
]
I.some(I.propEquals('name', 'Alice'), users)
// => true
See also: propEquals
<V>(defaultValue: V) => <K extends string>(key: K) => <T extends HasKey<K, V>>(object: T) => V | Defined<T[K]>
Like prop, but if the resolved value is undefined
, defaultValue
is returned instead.
Example:
I.propOr(999, 'a', { a: 1, b: 2, c: 3 })
// => 1
I.propOr(999, 'a', {})
// => 999
I.propOr(999, 'a', { a: undefined })
// => 999
<K extends string>(key: K) => <V>(predicate: (value: V) => boolean) => <T extends HasKey<K, V>>(object: T) => boolean
Check if property key
of object
satisfies the predicate
.
Example:
const users = [
{ name: 'Alice' },
{ name: 'Bob' },
]
I.some(I.propSatisfies('name', I.test(/^A/)), users)
// => true
See also: propEquals
<K extends string>(key: K) => <T extends HasKey<K>>(object: T) => Omit<T, K>
Return a copy of object
without the property key
.
- If
key
is not an own property ofobject
, theobject
is returned unchanged.
Example:
I.removeProp('a', { a: 1, b: 2, c: 3 })
// => { b: 2, c: 3 }
<K extends string>(key: K) => <V>(value: V) => <T extends HasKey<K, V>>(object: T) => T
Return a copy of object
with property key
set to value
.
- If
value
isundefined
, the property is removed.
Example:
I.setProp('a', 999, { a: 1, b: 2, c: 3 })
// => { a: 999, b: 2, c: 3 }
I.setProp('a', undefined, { a: 1, b: 2, c: 3 })
// => { b: 2, c: 3 }
See also: modifyProp, removeProp
<T extends object>(object: T) => Array<T[keyof T & string]>
Return an array of the own enumerable property values of object
Example:
I.keys({ a: 1, b: 2, c: 3 })
// => [1, 2, 3]
<T1, T2, R>(fn: VariadicFunction2<T1, T2, R>) => Function2<T1, T2, R>
Create a version of fn
that accepts two arguments.
Note: The returned function is not curried.
Example:
const fn = (...args) => args
const wrapped = I.binary(fn)
fn(1, 2, 3)
// => [1, 2, 3]
wrapped(1, 2, 3)
// => [1, 2]
See also: unary
<T extends VariadicFunction0<boolean>>(fn: T) => T
Create a version of a predicate fn
that flips the returned boolean value.
Example:
const isZero = (v) => v === 0
const notZero = I.complement(isZero)
notZero(0)
// => false
notZero(1)
// => true
<T extends unknown[], R>(fn: (args: ...T) => R) => (args: ...T) => R
<T extends unknown[], T1, R>(fn1: Function1<T1, R>, fn2: (args: ...T) => T1) => (args: ...T) => R
<T extends unknown[], T1, T2, R>(fn1: Function1<T2, R>, fn2: Function1<T1, T2>, fn3: (args: ...T) => T1) => (args: ...T) => R
Right-to-left function composition.
Note: This function is not curried.
Example:
const composed = I.compose(I.add(10), I.multiply(2))
composed(2)
// => 14
<T>(value: T) => () => T
Create a function that always returns value
.
Example:
I.map(I.constant(1), [1, 2, 3])
// => [1, 1, 1]
<T extends [unknown, unknown], R>(fn: (args: ...T) => R) => CurriedFunction2<T, R>
Create a curried version of a fn
taking two arguments.
Example:
const add = I.curry2((a, b) => a + b)
add(1)(2)
// => 3
add(1, 2)
// => 3
<T extends [unknown, unknown, unknown], R>(fn: (args: ...T) => R) => CurriedFunction3<T, R>
Create a curried version of a fn
taking three arguments.
Example:
const add = I.curry3((a, b, c) => a + b + c)
add(1)(2)(3)
// => 6
add(1, 2, 3)
// => 6
<T extends [unknown, unknown, unknown, unknown], R>(fn: (args: ...T) => R) => CurriedFunction4<T, R>
Create a curried version of a fn
taking four arguments.
Example:
const add = I.curry4((a, b, c, d) => a + b + c + d)
add(1)(2)(3)(4)
// => 10
add(1, 2, 3, 4)
// => 10
<T, U, R>(fn: Function2<T, U, R>) => Function2<U, T, R>
Flip the arguments of a binary function.
Note: The returned function is not curried.
Example:
const fn = (...args) => args
const flipped = I.flip(fn)
flipped(1, 2)
// => [2, 1]
<T>(value: T) => T
Identity function. Returns the first argument.
Example:
I.identity(5)
// => 5
() => undefined
Do nothing an return undefined
.
Example:
I.map(I.noop, [1, 2, 3])
// => [undefined, undefined, undefined]
(bool: boolean) => boolean
Logical not. Flip the value of a boolean argument
Example:
I.not(true)
// => false
I.not(false)
// => true
See also: complement
<T>(initial: T) => T
<T, R>(initial: T, fn1: Function1<T, R>) => R
<T1, T2, R>(initial: T1, fn1: Function1<T1, T2>, fn2: Function1<T2, R>) => R
Pipe an initial
value through one or more functions in left-to-right order,
allowing the programmer to chain operations in a readable manner.
I.pipe(initial, f1, f2, ...fn)
can be thought as syntax sugar
for fn(...(f2(f1(initial))))
Note: This function is not curried.
Example:
I.pipe(
[1, 2, 3],
I.map((n) => n * 2),
I.sum
)
// => 12
See also: compose
<T>(first: unknown, second: T) => T
Return the second
argument.
Example:
I.second(1, 2)
// => 2
<T>(fn: (value: T) => void) => (value: T) => T
Create a function that applies fn
to its argument and returns the
argument.
Useful for executing a side-effect within a pipeline.
Example:
I.pipe(
[1, 2, 3],
I.map(I.multiply(2)),
I.filter(I.gt(2)),
I.tap(console.log),
I.sum
)
// Prints: [ 4, 6 ]
// => 10
<T, R>(fn: VariadicFunction1<T, R>) => Function1<T, R>
Create a version of fn
that accepts a single argument.
Example:
['1', '2', '3'].map(I.unary(parseInt))
// => [1, 2, 3]
See also: binary
<T extends Ordered>(interval: [lower: T, upper: T]) => (value: T) => T
Clamp a number within the closed interval [lower, upper]
.
Example:
I.clamp([0, 10], 5)
// => 5
I.clamp([0, 10], 15)
// => 10
I.clamp([0, 10], -5)
// => 0
<T>(first: T) => (second: T) => boolean
Check if two values are deeply equal.
- Primitive values are compared with SameValueZero.
- Only the own enumerable keys of objects are considered.
- The order of object keys does not matter.
- Built-in objects (e.g. Arrays, Maps & Sets) are not checked for extra keys.
- Sets and Map keys are compared with SameValueZero.
- Error objects are equal if their
name
andmessage
properties are equal. - Functions are compared with
===
. - Supports cyclic references.
- Does not support WeakMaps, WeakSets or typed arrays.
Example:
I.equals([1, 2, 3], [1, 2, 3])
// => true
I.equals([1, 2, 3], [4, 5, 6])
// => false
<T, U>(fn: (value: T) => U) => (first: T) => (second: T) => boolean
Like equals, but the function fn
is applied to both values before
determining equality.
Example:
I.equalsBy(Math.floor, 1, 1.5)
// => true
See also: equals
<T extends Ordered>(first: T) => (second: T) => boolean
Check if the second
argument is greater than the first
.
Designed to be used as a curried predicate.
Example:
I.filter(I.gt(2), [1, 2, 3])
// => [3]
<T extends Ordered>(first: T) => (second: T) => boolean
Check if the second
argument is greater than or equal to the first
.
Designed to be used as a curried predicate.
Example:
I.filter(I.gte(2), [1, 2, 3])
// => [2, 3]
<T extends Ordered>(first: T) => (second: T) => boolean
Check if the second
argument is less than the first
.
Designed to be used as a curried predicate.
Example:
I.filter(I.lt(2), [1, 2, 3])
// => [1]
<T extends Ordered>(first: T) => (second: T) => boolean
Check if the second
argument is less than or equal to the first
.
Designed to be used as a curried predicate.
Example:
I.filter(I.lte(2), [1, 2, 3])
// => [1, 2]
<T extends Ordered>(first: T) => (second: T) => T
Return the larger of two values.
Example:
I.max(1, 2)
// => 2
I.max('a', 'b')
// => 'b'
<T, U extends Ordered>(fn: (value: T) => U) => (first: T, second: T) => T
Like max, but apply fn
to both values before determining their
ordering.
Example:
I.maxBy(Math.abs, 1, -2)
// => -2
<T extends Ordered>(first: T) => (second: T) => T
Return the smaller of two values.
Example:
I.min(1, 2)
// => 1
I.min('a', 'b')
// => 'a'
<T, U extends Ordered>(fn: (value: T) => U) => (first: T) => (second: T) => T
Like min, but apply fn
to both values before determining their
ordering.
Example:
I.minBy(Math.abs, -1, 2)
// => -1
(n: number) => (m: number) => number
Add two numbers together.
Example:
I.map(I.add(1), [1, 2, 3])
// => [2, 3, 4]
(n: number) => number
Decrement a number by 1.
Example:
I.map(I.dec, [1, 2, 3])
// => [0, 1, 2]
See also: inc
(divisor: number) => (dividend: number) => number
Divide dividend
by the divisor
.
Example:
I.map(I.divideBy(2), [1, 2, 3])
// => [0.5, 1, 1.5]
(n: number) => number
Increment a number by 1.
Example:
I.map(I.inc, [1, 2, 3])
// => [2, 3, 4]
(multiplicand: number) => (multiplier: number) => number
Multiply two numbers together.
Example:
I.map(I.multiply(2), [1, 2, 3])
// => [2, 4, 6]
(n: number) => number
Return n
with its sign reversed.
Example:
I.map(I.negate, [1, 2, 3])
// => [-1, -2, -3]
(subtrahend: number) => (minuend: number) => number
Subtract the subtrahend
from the minuend
.
Example:
I.map(I.subtractBy(1), [1, 2, 3])
// => [0, 1, 2]
<R>(defaultValue: R) => <T>(fn: (value: T) => R) => (maybeValue: undefined | T) => R
Apply fn
to maybeValue
if it is not undefined
, return defaultValue
otherwise.
Example:
I.maybe('', (s) => s.toUpperCase(), 'hi')
// => 'HI'
I.maybe('', (s) => s.toUpperCase(), undefined)
// => ''
See also: valueOr
<T>(defaultValue: T) => (maybeValue: T | undefined) => T
Return maybeValue
if it is not undefined
, defaultValue
otherwise.
Example:
I.valueOr(999, 0)
// => 0
I.valueOr(999, undefined)
// => 999
See also: maybe
(string: string) => string
Convert the first code point of string
to uppercase and the rest to
lowercase.
Example:
I.capitalize('aBc')
// => 'Abc'
See also: toLowerCase, toUpperCase
(separator: RegExp | string) => (string: string) => string
Split the string
into an array of substrings between each separator
.
Example:
I.split(', ', 'a, b, c')
// => ['a', 'b', 'c']
See also: join
(regexp: RegExp) => (string: string) => boolean
Check if string
matches the regexp
.
Example:
I.test(/abc/, 'abc')
// => true
(string: string) => string
Convert string
to lowercase.
Example:
I.toLowerCase('ABC')
// => 'abc'
See also: toUpperCase, capitalize
(string: string) => string
Convert string
to uppercase.
Example:
I.toUpperCase('abc')
// => 'ABC'
See also: toLowerCase, capitalize
(string: string) => string
Remove whitespace from both ends of a string
.
Example:
I.trim(' abc ')
// => 'abc'
(string: string) => string
Remove whitespace from the end of a string
.
Example:
I.trimEnd(' abc ')
// => ' abc'
(string: string) => string
Remove whitespace from the beginning of a string
.
Example:
I.trimStart(' abc ')
// => 'abc '
<T>(value: T | unknown[]) => value is unknown[]
Check if the value
is an
Array
.
<T>(value: T | bigint) => value is bigint
Check if the value
is a
BigInt
.
<T>(value: T | boolean) => value is boolean
Check if the value
is a
boolean
.
<T>(value: T | Date) => value is Date
Check if the value
is a
Date
.
<T>(value: T | undefined) => value is T
Check if the value
is not
undefined
.
<T>(value: T | Error) => value is Error
Check if the value
is an
Error
.
<T>(value: T | Function) => value is Function
Check if the value
is a
function.
<T>(value: T | Map<unknown, unknown>) => value is Map<unknown, unknown>
Check if the value
is a
Map
.
<T>(value: T | null | undefined) => value is undefined | null
Check if the value
is
null
or
undefined
.
<T>(value: T | null) => value is null
Check if the value
is
null
.
<T>(value: T | number) => value is number
Check if the value
is a
number
.
<T>(value: T | object) => value is object
Check if the value
is an
object.
Note that functions and arrays are also objects.
<T>(value: T | RegExp) => value is RegExp
Check if the value
is a
RegExp
.
<T>(value: T | Set<unknown>) => value is Set<unknown>
Check if the value
is a
Set
.
<T>(value: T | string) => value is string
Check if the value
is a
string
.
<T>(value: T | symbol) => value is symbol
Check if the value
is a
Symbol
.
<T>(value: T | undefined) => value is undefined
Check if the value
is
undefined
.