Skip to main content

v1 (Legacy)

Migrating to v2

Migrating to v2

v2 is the first major version release of Remeda in almost 2 years. We took this opportunity to gather as many breaking changes as possible into a single release, focusing on modernization and simplification. Importantly, this release doesn’t change any major aspect of Remeda’s architecture. Almost half of Remeda’s exported functions don’t have any breaking changes, neither in their runtime nor their typing!

Migrating

For most projects, only minor changes to how some functions are called will be necessary.

Some functions have parameter types or return types that have changed; these may require adjustments either upstream or downstream from where the function is called. These changes need more attention as they might expose existing bugs in your codebase (similar to better typing or a new lint rule).

A few functions have breaking changes in their runtime implementation, mainly in how edge-cases are handled. These also require careful attention during migration to ensure the new implementation behaves as expected.

To facilitate this process, we provide a function-by-function migration guide that details the changes for each function, includes examples of potential breakages, and offers solutions, including ways to maintain previous behaviors.

We recommend first updating to the latest v1 version, fixing any deprecation errors, and then updating to the latest version of Remeda, using this guide to address any remaining issues.

The following chapters provide an overview of the changes, offering a broader perspective and motivation for each change. All relevant information for each function is repeated in each function’s migration documentation.

Environment

Remeda v2 is built for modern environments. Browsers and runtimes that don’t support the minimum requirements might still be able to use some functions (if their implementation doesn’t rely on anything more modern), but those cases will not be supported.

Runtime ≥ ES2022

Previously, Remeda compiled down to a target of ES5 (and ES2017 lib). This meant that modern JavaScript features (like object and array spreading) had to be polyfilled and shipped with each function that used them. It also meant that we couldn’t use certain features, like built-in iterators (e.g., Array.prototype.entries) or bigints.

v2 is compiled with a target of ES2022 (and ES2022 lib), which is supported by all currently maintained Node.js versions (18+) and by ~93.8% of all browsers.

TypeScript ≥ 5.1

The minimum TypeScript version our exported types are tested against is 5.1, up from 4.2 in v1.

We currently don’t use any new language features that were only added in recent versions of TypeScript, but we might want to use them in the future without requiring a breaking change.

Importing

Remeda v2 builds its packaged files using tsdown (replacing the bare tsc build of the previous version), with full support for tree-shaking, code splitting, and minification. The output config is validated using both attw and publint. This results in completely different output artifacts and structure for both CommonJS and ESM. We don’t expect this to have any impact on your project; it should integrate cleanly with any modern JS build tool, bundler, and runtime.

Removed Variants

In v1, Remeda offered several “variants” of the base runtime implementation and typing via properties added to the exported function. These have been removed in v2, and their usage merged into the base functions.

Indexed

The indexed variant allowed callback functions (predicates, mappers, etc.) to use 2 additional parameters in their signature: the index, representing the offset of the item within the data array, and the data array itself. These were provided to most functions but weren’t offered consistently. The implementation added runtime checks on every invocation, even when the indexed variant wasn’t used. In v2, the indexed “variant” of the callback is now available on the base implementation and has been added to all functions. This aligns with the signatures of the built-in functions of Array.prototype.

const DATA = [1, 2, 3] as const;

// Was
map.indexed(DATA, (item, index) => item + index);

// Now
map(DATA, (item, index) => item + index);

Object-based functions (like mapKeys) also got the same treatment, where the callbacks are called with the prop’s key as the 2nd parameter (instead of the numerical index for arrays).

Migration

For calls that used the indexed variant, simply remove the .indexed suffix. For the rest, you most likely don’t need to do anything.

Note: If the callback function was passed by reference and not via an inline function, your callback function would now be called with additional parameters. If the function signature no longer matches, TypeScript would complain about the type mismatch. In more complex cases, if the function signature does match, it will now be called with additional parameters and might compute results differently. This is rare and can only happen if the callback function already accepted an optional number or number | undefined as its second parameter. To fix this, simply wrap the callback with an inline function that takes a single parameter. ESLint’s Unicorn plugin’s unicorn/no-array-callback-reference is recommended to detect potential cases of this issue.

const DATA = ["1", "2", "3"] as const;

// BUG! `parseInt` takes an optional 2nd `number` param for the radix!
map(DATA, Number.parseInt); //=> [1, NaN, NaN]

// Fix:
map(DATA, (item) => Number.parseInt(item)); //=> [1, 2, 3]

Strict

We sometimes come up with improved typing for a function’s return value. The type is often more complex and makes more assumptions about the inputs, making it incompatible with the existing type. In these cases, we created a strict variant with the same runtime implementation but with improved typing. In v2, all strict variants are now the default, removing the original base typing.

This change can result in downstream assumptions about types breaking or becoming invalid. In most cases, we believe these are valid typing issues being surfaced for the first time because of the improved typing.

const DATA = ["1", "2", "3"] as const;

const was = map(DATA, (item) => Number.parseInt(item));
//    ^? number[];

const now = map(DATA, (item) => Number.parseInt(item));
//    ^? [number, number, number]
Migration

For calls that used the strict variant, simply remove the .strict suffix. For the rest, you most likely don’t need to do anything.

If you encounter new TypeScript issues following this change, we recommend first checking if this issue is the result of the better typing. Note that if you use inferred typing a lot, the issue might only surface further downstream and not at the place the function is called.

To bypass or work around these issues:

  • The function-specific migration guides below also suggest possible type assertions that could be used to get the “legacy” types back.
  • Simplify the types by using the TypeScript satisfies keyword instead of as const.
  • You can use explicit, less specific types in the generics of the functions to force them to a specific type instead of the inferred type.
  • Most of the new types should be extendable by the old types, meaning you can cast the output to the type you expect to simplify the result.
  • Some new types might be hard to read and understand via the IDE’s tooltips. In those cases, you can use Type-Fest’s Simplify to debug the resulting type (in most cases, we already wrap the types with Simplify).

Important: The types might have edge cases that we didn’t foresee and test against. If you feel that the computed type is wrong, please report it on GitHub.

// Downstream bugs revealed:

// @ts-expect-error [ts(2493)]Tuple type '[number]' of length '1' has no element at index '1'.
const [, buggy] = map(["1"] as const, (x) => Number.parseInt(x));

// Get the legacy behavior:

const strict = map(["1", "2", "3"] as const, (x) => Number.parseInt(x));
//    ^? [number, number, number]

const satisfied = map(["1", "2", "3"] satisfies `${number}`[], (x) =>
  //  ^? number[];
  Number.parseInt(x),
);

const generalized = map<`${number}`[], number[]>(
  ["1", "2", "3"] as const,
  (x) =>
    //  ^? number[]
    Number.parseInt(x),
);

const casted = map(["1", "2", "3"] as `${number}`[], (x) =>
  //  ^? number[];
  Number.parseInt(x),
);

const castedOutput = map(["1", "2", "3"] as const, (x) =>
  Number.parseInt(x),
) as number[];

Lazy (Internal)

The lazy variant wasn’t documented but still existed on many functions. Unlike the previous variants, it wasn’t another implementation of the function, but a tool used internally by the purry and pipe functions to allow lazy evaluation of functions. This abstraction has been completely removed.

Migration

If you exported a lazy property from your internal functions to make them lazy within Remeda’s pipe, use purry with the lazy implementation as the 3rd parameter instead.

We consider this API internal and thus don’t provide documentation or export the types and utilities that would make it easier to work with (the ones we use internally). If you need these APIs, please open an issue on GitHub.

Headless Invocation

A few single-parameter functions in v1 did not offer a properly curried “dataLast” implementation and instead suggested using a “headless” version for “dataLast” cases (e.g., keys). This created problems with more advanced types not being inferred correctly, requiring a properly curried version instead (e.g., first). We felt that this case-by-case difference made your code more error-prone and confusing. In v2, all single-parameter functions should now be called with no parameters to get their dataLast implementation.

The only headless functions remaining are type-guards (e.g., isString, isDefined).

// Was
pipe(DATA, keys);
map(DATA, identity);
filter(DATA, isString);

// Now
pipe(DATA, keys());
map(DATA, identity());
filter(DATA, isString); // Not changed!

Migration

Most call sites should now show an error when using the headless function because TypeScript wouldn’t be able to infer the type correctly. However, because there is no way to deprecate the “headless” nature of a function (it’s just a function-object), you will have to manually search for them. The functions are: clone, identity, fromPairs*, keys, randomString, toPairs*, and values.

* These functions have been renamed and their renamed versions already don’t support headless invocation in v1.

Renamed and Removed

Removed

To offer the best possible functions, we deemed several functions as redundant when they could be easily replaced with other existing functions, resulting in code of the same length. In all these cases, the replacement is a composite of at most three functions.

// Was
compact(DATA);

// Now
filter(DATA, isTruthy);

Other functions were removed because their logic was either split into several other functions or merged into a more general-purpose tool to allow better code reuse and improved typing.

// Was
flatten(DATA);
flattenDeep(DATA);

// Now
flat(DATA);
flat(DATA, 10);

The functions are: compact, countBy, flatMapToObj, flatten, flattenDeep, isObject, maxBy, minBy, noop, reject, type, and zipObj.

Renamed

Remeda took a lot of its early inspiration from Lodash and Ramda. Many functions were named similarly to their equivalents in those libraries, but these names don’t always align with the names chosen by the ECMAScript standard. We chose to prefer the standard names.

// Was
pipe(DATA, toPairs(), ..., fromPairs());

// Now
pipe(DATA, entries(), ..., fromEntries())

We also decided to improve some names by dropping abbreviations and partial spellings in favor of proper English words.

// Was
uniq(DATA);

// Now
unique(DATA);

The functions are: createPipe, equals, fromPairs, isNil, toPairs, uniq, uniqBy, and uniqWith.

Migration

The latest versions of Remeda v1 have all renamed and removed functions deprecated with suggestions for how to migrate. Doing this while still in v1 would make it easier to replace them one-by-one. Otherwise, this document has a deprecated section with migration instructions too.

Object Keys

Most of the functions that provided a way to traverse objects relied on the built-in Object.entries. This function has limitations on which properties it iterates upon (enumerates):

  • number keys are cast as string.
  • symbol keys are ignored.

To properly reflect this, we had to change the typing for both the callback functions and the return types. Functions that returned an object would either drop the symbol keys (if constructing a new object) or copy them as-is (if cloning the original object).

It’s important to note that only the types have changed here; the runtime behavior remains the same. number keys are always cast as strings in JavaScript; myObj[0] and myObj["0"] access the same property. This change will not construct your objects differently than they used to be. To provide more utility, the implementations of omit and omitBy have been changed to preserve symbol keys.

Read more about this on MDN.

Migration

The biggest differences are due to the change in how we handle symbol keys. symbol usage is rare, and if you don’t know you use it in your project, you most likely don’t.

number keys require a little more attention, especially if you are checking or using the keys by value (and not just passing them around). Because only types have changed (and not the runtime behavior), you might run into new TypeScript (or ESLint) warnings and errors due to surfacing previously existing issues.

The affected functions are: entries, evolve, forEachObj, keys, mapKeys, mapValues, omit, omitBy, and pickBy.

Re-Implementations

Several functions had their runtime implementation changed, including changes to their semantics, so that they’d return different results in v2 for some edge cases. These changes are documented below for each function. The functions are: clone, difference, intersection, omit, omitBy, purry, sample, and zipWith.


Array

allPass

Determines whether all predicates returns true for the input data.

Data First

R.allPass(data, fns);
Parameters
data
The input data for predicates.
fns
The list of predicates.
Returns
boolean
const isDivisibleBy3 = (x: number) => x % 3 === 0;
const isDivisibleBy4 = (x: number) => x % 4 === 0;
const fns = [isDivisibleBy3, isDivisibleBy4];
R.allPass(12, fns); // => true
R.allPass(8, fns); // => false

Data Last

R.allPass(fns)(data);
Parameters
fns
The list of predicates.
Returns
Object
const isDivisibleBy3 = (x: number) => x % 3 === 0;
const isDivisibleBy4 = (x: number) => x % 4 === 0;
const fns = [isDivisibleBy3, isDivisibleBy4];
R.allPass(fns)(12); // => true
R.allPass(fns)(8); // => false

anyPass

Determines whether any predicate returns true for the input data.

Data First

R.anyPass(data, fns);
Parameters
data
The input data for predicates.
fns
The list of predicates.
Returns
boolean
const isDivisibleBy3 = (x: number) => x % 3 === 0;
const isDivisibleBy4 = (x: number) => x % 4 === 0;
const fns = [isDivisibleBy3, isDivisibleBy4];
R.anyPass(8, fns); // => true
R.anyPass(11, fns); // => false

Data Last

R.anyPass(fns)(data);
Parameters
fns
The list of predicates.
Returns
Object
const isDivisibleBy3 = (x: number) => x % 3 === 0;
const isDivisibleBy4 = (x: number) => x % 4 === 0;
const fns = [isDivisibleBy3, isDivisibleBy4];
R.anyPass(fns)(8); // => true
R.anyPass(fns)(11); // => false

chunk

Split an array into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements.

Data First

R.chunk(array, size);
Parameters
array
The array.
size
The length of the chunk.
Returns
Object
R.chunk(["a", "b", "c", "d"], 2); // => [['a', 'b'], ['c', 'd']]
R.chunk(["a", "b", "c", "d"], 3); // => [['a', 'b', 'c'], ['d']]

Data Last

R.chunk(size)(array);
Parameters
size
The length of the chunk.
Returns
Object
R.chunk(2)(["a", "b", "c", "d"]); // => [['a', 'b'], ['c', 'd']]
R.chunk(3)(["a", "b", "c", "d"]); // => [['a', 'b', 'c'], ['d']]

concat

Combines two arrays.

Breaking changes in v2

Typing

The return type was made stricter. If the 2 arrays have different types of items, or the inputs are tuples, the new output type maintains more of the shape of the input.

Examples
const a = [1, 2, 3] as const;
const b = ["hello", "world"] as string[];

const concatenated = concat(a, b);
//    ^? [1, 2, 3, ...string[]], was: (string | 1 | 2 | 3)[]

Data First

R.concat(arr1, arr2);
Parameters
arr1
The first array.
arr2
The second array.
Returns
Array
R.concat([1, 2, 3], ["a"]); // [1, 2, 3, 'a']

Data Last

R.concat(arr2)(arr1);
Parameters
arr2
The second array.
Returns
Object
R.concat(["a"])([1, 2, 3]); // [1, 2, 3, 'a']

difference

Lazy

Excludes the values from other array. The output maintains the same order as the input. If either array or other contain multiple items with the same values, all occurrences of those values will be removed. If the exact number of copies should be observed (i.e. multi-set semantics), use R.difference.multiset instead. If the arrays don't contain duplicates, both implementations yield the same result.

! DEPRECATED: Use R.difference.multiset(data, other) (or R.filter(data, R.isNot(R.isIncludedIn(other))) to keep the current runtime logic). R.difference.multiset will replace R.difference in v2!

Breaking changes in v2

Runtime

The previous implementation did not have a predictable handling of inputs with duplicate values (e.g. [1, 1], aka bag/multi-set). This could lead to confusing results, and did not enable using the function for multi-set use cases.

In V2 the default implementation now takes into account the duplication factor of the items in both input arrays.

If your data had no duplications the two implementations are equivalent.

If you want to maintain the same runtime behavior for dealing with duplicates use filter with isNot and isIncludedIn as the predicate.

Examples
No duplicates
// Was
difference([1, 2, 3], [2]); // => [1, 3]

// Now
difference([1, 2, 3], [2]); // => [1, 3]
Duplicates
// Was
difference([1, 1, 2, 2], [1]); // => [2, 2]

// Now
difference([1, 1, 2, 2], [1]); // => [1, 2, 2]
Legacy (dataFirst)
// Was
difference([1, 1, 2, 2], [1]); // => [2, 2]

// Now
filter([1, 1, 2, 2], isNot(isIncludedIn([1]))); // => [2, 2]
Legacy (dataLast)
// Was
pipe([1, 1, 2, 2], difference([1]));

// Now
pipe([1, 1, 2, 2], filter(isNot(isIncludedIn([1]))));

Data First

R.difference(data, other);
Parameters
data
The input items.
other
The values to exclude.
Returns
Array
R.difference([1, 2, 3, 4], [2, 5, 3]); // => [1, 4]
R.difference([1, 1, 2, 2], [1]); // => [2, 2]
R.difference.multiset([1, 1, 2, 2], [1]); // => [1, 2, 2]

Data First

R.difference(other)(data);
Parameters
other
The values to exclude.
Returns
Object
R.pipe([1, 2, 3, 4], R.difference([2, 5, 3])); // => [1, 4]
R.pipe([1, 1, 2, 2], R.difference([1])); // => [2, 2]
R.pipe([1, 1, 2, 2], R.difference.multiset([1])); // => [1, 2, 2]

differenceWith

Lazy

Excludes the values from other array. Elements are compared by custom comparator isEquals.

Data First

R.differenceWith(array, other, isEquals);
Parameters
array
The source array.
other
The values to exclude.
isEquals
The comparator.
Returns
Array
R.differenceWith(
  [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }],
  [{ a: 2 }, { a: 5 }, { a: 3 }],
  R.equals,
); // => [{a: 1}, {a: 4}]

Data Last

R.differenceWith(other, isEquals)(array);
Parameters
other
The values to exclude.
isEquals
The comparator.
Returns
Object
R.differenceWith(
  [{ a: 2 }, { a: 5 }, { a: 3 }],
  R.equals,
)([{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }]); // => [{a: 1}, {a: 4}]
R.pipe(
  [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }, { a: 5 }, { a: 6 }], // only 4 iterations
  R.differenceWith([{ a: 2 }, { a: 3 }], R.equals),
  R.take(2),
); // => [{a: 1}, {a: 4}]

drop

Lazy

Removes first n elements from the array.

Data First

R.drop(array, n);
Parameters
array
The target array.
n
The number of elements to skip.
Returns
Array
R.drop([1, 2, 3, 4, 5], 2); // => [3, 4, 5]

Data Last

R.drop(n)(array);
Parameters
n
The number of elements to skip.
Returns
Object
R.drop(2)([1, 2, 3, 4, 5]); // => [3, 4, 5]

dropFirstBy

Drop the first n items from data based on the provided ordering criteria. This allows you to avoid sorting the array before dropping the items. The complexity of this function is O(Nlogn) where N is the length of the array.

For the opposite operation (to keep n elements) see takeFirstBy.

Data First

R.dropFirstBy(data, n, ...rules);
Parameters
data
The input array.
n
The number of items to drop. If `n` is non-positive no items would be dropped and a *clone* of the input would be returned, if `n` is bigger then data.length no items would be returned.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Array
R.dropFirstBy(["aa", "aaaa", "a", "aaa"], 2, (x) => x.length); // => ['aaa', 'aaaa']

Data Last

R.dropFirstBy(n, ...rules)(data);
Parameters
n
The number of items to drop. If `n` is non-positive no items would be dropped and a *clone* of the input would be returned, if `n` is bigger then data.length no items would be returned.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
R.pipe(
  ["aa", "aaaa", "a", "aaa"],
  R.dropFirstBy(2, (x) => x.length),
); // => ['aaa', 'aaaa']

dropLast

Removes last n elements from the array.

Data First

R.dropLast(array, n);
Parameters
array
The target array.
n
The number of elements to skip.
Returns
Array
R.dropLast([1, 2, 3, 4, 5], 2); // => [1, 2, 3]

Data Last

R.dropLast(n)(array);
Parameters
n
The number of elements to skip.
Returns
Object
R.dropLast(2)([1, 2, 3, 4, 5]); // => [1, 2, 3]

dropLastWhile

Removes elements from the end of the array until the predicate returns false.

The predicate is applied to each element in the array starting from the end and moving towards the beginning, until the predicate returns false. The returned array includes elements from the beginning of the array, up to and including the element that produced false for the predicate.

Data First

R.dropLastWhile(data, predicate);
Parameters
data
The array.
predicate
The predicate.
Returns
Array
R.dropLastWhile([1, 2, 10, 3, 4], (x) => x < 10); // => [1, 2, 10]

Data Last

R.dropLastWhile(predicate)(data);
Parameters
predicate
The predicate.
Returns
Object
R.pipe(
  [1, 2, 10, 3, 4],
  R.dropLastWhile((x) => x < 10),
); // => [1, 2, 10]

dropWhile

Removes elements from the beginning of the array until the predicate returns false.

The predicate is applied to each element in the array, until the predicate returns false. The returned array includes the rest of the elements, starting with the element that produced false for the predicate.

Data First

R.dropWhile(data, predicate);
Parameters
data
The array.
predicate
The predicate.
Returns
Array
R.dropWhile([1, 2, 10, 3, 4], (x) => x < 10); // => [10, 3, 4]

Data Last

R.dropWhile(predicate)(data);
Parameters
predicate
The predicate.
Returns
Object
R.pipe(
  [1, 2, 10, 3, 4],
  R.dropWhile((x) => x < 10),
); // => [10, 3, 4]

filter

IndexedLazy

Filter the elements of an array that meet the condition specified in a callback function.

Breaking changes in v2

Typing

The indexed variant was removed; the base implementation takes the same parameters. If you are using indexed you can simply remove it without any other changes.

Runtime

The predicate function now takes 2 additional parameters: index - The index of the current element being processed in array, and data - the array the function was called upon (the same signature as the built-in Array.prototype.filter).

If you are using a function reference for the predicate (and not an inline arrow function), and that function accepts more than one param you might run into compile-time (or run-time!) issues because of the extra params being sent on each invocation of the function. We highly recommend using unicorn/no-array-callback-reference to warn against these issues.

Examples
Indexed variant removed
// Was
filter.indexed([1, 2, 3], (item, index) => item + (index % 2) === 0);

// Now
filter([1, 2, 3], (item, index) => item + (index % 2) === 0);
Potential bug
function callback(value: number, index = 0): boolean {
  return (value + index) % 2 === 0;
}

// Bug
filter([1, 2, 3], callback); // => [], Was: [2]

// Fix
filter([1, 2, 3], (item) => callback(item)); // => [2]

Data First

R.filter(array, fn);
Parameters
array
The array to filter.
fn
The callback function.
Returns
Array
R.filter([1, 2, 3], (x) => x % 2 === 1); // => [1, 3]
R.filter.indexed([1, 2, 3], (x, i, array) => x % 2 === 1); // => [1, 3]

Data Last

R.filter(fn)(array);
Parameters
fn
The callback function.
Returns
Object
R.pipe(
  [1, 2, 3],
  R.filter((x) => x % 2 === 1),
); // => [1, 3]
R.pipe(
  [1, 2, 3],
  R.filter.indexed((x, i) => x % 2 === 1),
); // => [1, 3]

find

IndexedLazy

Returns the value of the first element in the array where predicate is true, and undefined otherwise.

Breaking changes in v2

Typing

The indexed variant was removed; the base implementation takes the same parameters. If you are using indexed you can simply remove it without any other changes.

When using with a type-predicate, the returned item would now be narrowed to the type of the type-predicate, similar to how filter with a type-predicate narrows the resulting items in the array.

Runtime

The predicate function now takes 2 additional parameters: index - The index of the current element being processed in array, and data - the array the function was called upon (the same signature as the built-in Array.prototype.find).

If you are using a function reference for the predicate (and not an inline arrow function), and that function accepts more than one param you might run into compile-time (or run-time!) issues because of the extra params being sent on each invocation of the function. We highly recommend using unicorn/no-array-callback-reference to warn against these issues.

Examples
Indexed variant removed
// Was
find.indexed([1, 2, 3], (item, index) => item + (index % 2) === 0);

// Now
find([1, 2, 3], (item, index) => item + (index % 2) === 0);
Narrowed Result
const result = find([1, "a"] as (string | number)[], isString);
//    ^? string | undefined, Was: string | number | undefined
Potential bug
function callback(value: number, index = 0): boolean {
  return (value + index) % 2 === 0;
}

// Bug
find([1, 2, 3], callback); // => undefined, Was: 2

// Fix
find([1, 2, 3], (item) => callback(item)); // => 2

Data First

R.find(items, fn);
Parameters
array
The array.
fn
The predicate.
Returns
Object
R.find([1, 3, 4, 6], (n) => n % 2 === 0); // => 4
R.find.indexed([1, 3, 4, 6], (n, i) => n % 2 === 0); // => 4

Data Last

R.find(fn)(items);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 3, 4, 6],
  R.find((n) => n % 2 === 0),
); // => 4
R.pipe(
  [1, 3, 4, 6],
  R.find.indexed((n, i) => n % 2 === 0),
); // => 4

findIndex

IndexedLazy

Returns the index of the first element in the array where predicate is true, and -1 otherwise.

Data First

R.findIndex(items, fn);
Parameters
array
The array.
fn
The predicate.
Returns
number
R.findIndex([1, 3, 4, 6], (n) => n % 2 === 0); // => 2
R.findIndex.indexed([1, 3, 4, 6], (n, i) => n % 2 === 0); // => 2

Data Last

R.findIndex(fn)(items);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 3, 4, 6],
  R.findIndex((n) => n % 2 === 0),
); // => 2
R.pipe(
  [1, 3, 4, 6],
  R.findIndex.indexed((n, i) => n % 2 === 0),
); // => 2

findLast

IndexedLazy

Returns the value of the last element in the array where predicate is true, and undefined otherwise.

Data First

R.findLast(items, fn);
Parameters
array
The array.
fn
The predicate.
Returns
Object
R.findLast([1, 3, 4, 6], (n) => n % 2 === 1); // => 3
R.findLast.indexed([1, 3, 4, 6], (n, i) => n % 2 === 1); // => 3

Data Last

R.findLast(fn)(items);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 3, 4, 6],
  R.findLast((n) => n % 2 === 1),
); // => 3
R.pipe(
  [1, 3, 4, 6],
  R.findLast.indexed((n, i) => n % 2 === 1),
); // => 3

findLastIndex

IndexedLazy

Returns the index of the last element in the array where predicate is true, and -1 otherwise.

Data First

R.findLastIndex(items, fn);
Parameters
array
The array.
fn
The predicate.
Returns
number
R.findLastIndex([1, 3, 4, 6], (n) => n % 2 === 1); // => 1
R.findLastIndex.indexed([1, 3, 4, 6], (n, i) => n % 2 === 1); // => 1

Data Last

R.findLastIndex(fn)(items);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 3, 4, 6],
  R.findLastIndex((n) => n % 2 === 1),
); // => 1
R.pipe(
  [1, 3, 4, 6],
  R.findLastIndex.indexed((n, i) => n % 2 === 1),
); // => 1

first

Lazy

Gets the first element of array.

Data First

R.first(array);
Parameters
data
The array.
Returns
Object
R.first([1, 2, 3]); // => 1
R.first([]); // => undefined

Data Last

R.first()(array);
Parameters
Returns
Object
R.pipe(
  [1, 2, 4, 8, 16],
  R.filter((x) => x > 3),
  R.first(),
  (x) => x + 1,
); // => 5

firstBy

Find the first element in the array that adheres to the order rules provided. This is a superset of what a typical maxBy or minBy function would do as it allows defining "tie-breaker" rules when values are equal, and allows comparing items using any logic. This function is equivalent to calling R.first(R.sortBy(...)) but runs at O(n) instead of O(nlogn).

Use nthBy if you need an element other that the first, or takeFirstBy if you more than just the first element.

Data Last

R.firstBy(...rules)(data);
Parameters
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
const max = R.pipe([1, 2, 3], R.firstBy([R.identity, "desc"])); // => 3;
const min = R.pipe([1, 2, 3], R.firstBy(R.identity)); // => 1;

const data = [{ a: "a" }, { a: "aa" }, { a: "aaa" }] as const;
const maxBy = R.pipe(data, R.firstBy([(item) => item.a.length, "desc"])); // => { a: "aaa" };
const minBy = R.pipe(
  data,
  R.firstBy((item) => item.a.length),
); // => { a: "a" };

const data = [
  { type: "cat", size: 1 },
  { type: "cat", size: 2 },
  { type: "dog", size: 3 },
] as const;
const multi = R.pipe(data, R.firstBy(R.prop("type"), [R.prop("size"), "desc"])); // => {type: "cat", size: 2}

Data First

R.firstBy(data, ...rules);
Parameters
data
An array of items.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
const max = R.firstBy([1, 2, 3], [R.identity, "desc"]); // => 3;
const min = R.firstBy([1, 2, 3], R.identity); // => 1;

const data = [{ a: "a" }, { a: "aa" }, { a: "aaa" }] as const;
const maxBy = R.firstBy(data, [(item) => item.a.length, "desc"]); // => { a: "aaa" };
const minBy = R.firstBy(data, (item) => item.a.length); // => { a: "a" };

const data = [
  { type: "cat", size: 1 },
  { type: "cat", size: 2 },
  { type: "dog", size: 3 },
] as const;
const multi = R.firstBy(data, R.prop("type"), [R.prop("size"), "desc"]); // => {type: "cat", size: 2}

flat

Lazy

Creates a new array with all sub-array elements concatenated into it recursively up to the specified depth. Equivalent to the built-in Array.prototype.flat method.

Data First

R.flat(data);
Parameters
data
The items to flatten.
depth
The depth level specifying how deep a nested array structure should be flattened. Defaults to 1. Non literal values (those typed as `number`cannot be used. `Infinity`, `Number.POSITIVE_INFINITY` and `Number.MAX_VALUE` are all typed as `number` and can't be used either. For "unlimited" depth use a literal value that would exceed your expected practical maximum nesting level.
Returns
Object
R.flat([[1, 2], [3, 4], [5], [[6]]]); // => [1, 2, 3, 4, 5, [6]]
R.flat([[[1]], [[2]]], 2); // => [1, 2]

Data Last

R.flat()(data);
Parameters
depth
The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
Returns
Object
R.pipe([[1, 2], [3, 4], [5], [[6]]], R.flat()); // => [1, 2, 3, 4, 5, [6]]
R.pipe([[[1]], [[2]]], R.flat(2)); // => [1, 2]

flatMap

Lazy

Map each element of an array using a defined callback function and flatten the mapped result.

Data First

R.flatMap(array, fn);
Parameters
array
The array to map.
fn
The function mapper.
Returns
Array
R.flatMap([1, 2, 3], (x) => [x, x * 10]); // => [1, 10, 2, 20, 3, 30]

Data Last

R.flatMap(fn)(array);
Parameters
fn
The function mapper.
Returns
Object
R.pipe(
  [1, 2, 3],
  R.flatMap((x) => [x, x * 10]),
); // => [1, 10, 2, 20, 3, 30]

forEach

IndexedLazy

Iterate an array using a defined callback function. The original array is returned instead of void.

Data First

R.forEach(array, fn);
Parameters
array
The array.
fn
The callback function.
Returns
Array
R.forEach([1, 2, 3], (x) => {
  console.log(x);
}); // => [1, 2, 3]
R.forEach.indexed([1, 2, 3], (x, i) => {
  console.log(x, i);
}); // => [1, 2, 3]

Data Last

R.forEach(fn)(array);
Parameters
fn
The function mapper.
Returns
Object
R.pipe(
  [1, 2, 3],
  R.forEach((x) => {
    console.log(x);
  }),
); // => [1, 2, 3]
R.pipe(
  [1, 2, 3],
  R.forEach.indexed((x, i) => {
    console.log(x, i);
  }),
); // => [1, 2, 3]

groupBy

StrictIndexed

Splits a collection into sets, grouped by the result of running each value through fn.

Data First

R.groupBy(array, fn);
Parameters
items
The items to group.
fn
The grouping function. When `undefined` is returned the item would be skipped and not grouped under any key.
Returns
Object
R.groupBy(["one", "two", "three"], (x) => x.length); // => {3: ['one', 'two'], 5: ['three']}
R.groupBy.strict([{ a: "cat" }, { a: "dog" }] as const, prop("a")); // => {cat: [{a: 'cat'}], dog: [{a: 'dog'}]} typed Partial<Record<'cat' | 'dog', NonEmptyArray<{a: 'cat' | 'dog'}>>>
R.groupBy([0, 1], (x) => (x % 2 === 0 ? "even" : undefined)); // => {even: [0]}

Data Last

R.groupBy(fn)(array);
Parameters
fn
Returns
Object
R.pipe(
  ["one", "two", "three"],
  R.groupBy((x) => x.length),
); // => {3: ['one', 'two'], 5: ['three']}

hasAtLeast

Checks if the given array has at least the defined number of elements. When the minimum used is a literal (e.g. 3) the output is refined accordingly so that those indices are defined when accessing the array even when using typescript's 'noUncheckedIndexAccess'.

Data First

R.hasAtLeast(data, minimum);
Parameters
data
The input array.
minimum
The minimum number of elements the array must have.
Returns
boolean
R.hasAtLeast([], 4); // => false

const data: number[] = [1, 2, 3, 4];
R.hasAtLeast(data, 1); // => true
data[0]; // 1, with type `number`

Data Last

R.hasAtLeast(minimum)(data);
Parameters
minimum
The minimum number of elements the array must have.
Returns
Object
R.pipe([], R.hasAtLeast(4)); // => false

const data = [[1, 2], [3], [4, 5]];
R.pipe(
  data,
  R.filter(R.hasAtLeast(2)),
  R.map(([, second]) => second),
); // => [2,5], with type `number[]`

indexBy

StrictIndexed

Converts a list of objects into an object indexing the objects by the given key (casted to a string). Use the strict version to maintain the given key's type, so long as it is a valid PropertyKey.

There are several other functions that could be used to build an object from an array:

  • fromKeys - Builds an object from an array of keys and a mapper for values.
  • pullObject - Builds an object from an array of items with mappers for both keys and values.
  • fromEntries - Builds an object from an array of key-value pairs.
  • mapToObj - Builds an object from an array of items and a single mapper for key-value pairs. Refer to the docs for more details.

Data First

R.indexBy(array, fn);
Parameters
array
The array.
fn
The indexing function.
Returns
Object
R.indexBy(["one", "two", "three"], (x) => x.length); // => {"3": 'two', "5": 'three'}
R.indexBy.strict(["one", "two", "three"], (x) => x.length); // => {3: 'two', 5: 'three'}

Data Last

R.indexBy(fn)(array);
Parameters
fn
The indexing function.
Returns
Object
R.pipe(
  ["one", "two", "three"],
  R.indexBy((x) => x.length),
); // => {"3": 'two', "5": 'three'}
R.pipe(
  ["one", "two", "three"],
  R.indexBy.strict((x) => x.length),
); // => {3: 'two', 5: 'three'}

intersection

Lazy

Returns a list of elements that exist in both array. The output maintains the same order as the input. If either array or other contain multiple items with the same values, all occurrences of those values will be present. If the exact number of copies should be observed (i.e. multi-set semantics), use R.intersection.multiset instead. If the arrays don't contain duplicates, both implementations yield the same result.

! DEPRECATED: Use R.intersection.multiset(data, other) (or R.filter(data, R.isIncludedIn(other)) to keep the current runtime logic). R.intersection.multiset will replace R.intersection in v2!

Breaking changes in v2

Typing

The typing for the other operand was widened so that it could accept any value. This allows the returned type to be narrowed to items that are in the intersection (type-wise) of the two types.

Runtime

The previous implementation did not have a predictable handling of inputs with duplicate values (e.g. [1, 1], aka bag/multi-set). This could lead to confusing results, and did not enable using the function for multi-set use cases.

In V2 the default implementation now takes into account the duplication factor of the items in both input arrays.

If your data had no duplications the two implementations are equivalent.

If you want to maintain the same runtime behavior for dealing with duplicates use filter with isIncludedIn as the predicate.

Examples
Narrowed result
const DATA1 = [] as (string | number)[];
const DATA2 = [] as (number | boolean)[];

// Was a type error, now okay:
const intersected = intersection(DATA1, DATA2);
//    ^? number[];
No duplicates
// Was
intersection([1, 2, 3], [2]); // => [2]

// Now
intersection([1, 2, 3], [2]); // => [2]
Duplicates
// Was
intersection([1, 1, 2, 2], [1]); // => [1, 1]

// Now
intersection([1, 1, 2, 2], [1]); // => [1]
Legacy (dataFirst)
// Was
intersection([1, 1, 2, 2], [1]); // => [1, 1]

// Now
filter([1, 1, 2, 2], isIncludedIn([1])); // => [1, 1]
Legacy (dataLast)
// Was
pipe([1, 1, 2, 2], intersection([1]));

// Now
pipe([1, 1, 2, 2], filter(isIncludedIn([1])));

Data First

R.intersection(data, other);
Parameters
data
The input items.
other
The items to compare against.
Returns
Array
R.intersection([1, 2, 3], [2, 3, 5]); // => [2, 3]
R.intersection([1, 1, 2, 2], [1]); // => [1, 1]
R.intersection.multiset([1, 1, 2, 2], [1]); // => [1]

Data First

R.intersection(other)(data);
Parameters
other
The items to compare against.
Returns
Object
R.pipe([1, 2, 3], R.intersection([2, 3, 5])); // => [2, 3]
R.pipe([1, 1, 2, 2], R.intersection([1])); // => [1, 1]
R.pipe([1, 1, 2, 2], R.intersection.multiset([1])); // => [1]

intersectionWith

Lazy

Returns a list of intersecting values based on a custom comparator function that compares elements of both arrays.

Data First

R.intersectionWith(array, other, comparator);
Parameters
array
The source array.
other
The second array.
comparator
The custom comparator.
Returns
Array
R.intersectionWith(
  [
    { id: 1, name: "Ryan" },
    { id: 3, name: "Emma" },
  ],
  [3, 5],
  (a, b) => a.id === b,
); // => [{ id: 3, name: 'Emma' }]

Data Last

R.intersectionWith(other, comparator)(array);
Parameters
other
The second array.
comparator
The custom comparator.
Returns
Object
R.intersectionWith(
  [3, 5],
  (a, b) => a.id === b,
)([
  { id: 1, name: "Ryan" },
  { id: 3, name: "Emma" },
]); // => [{ id: 3, name: 'Emma' }]

join

Joins the elements of the array by: casting them to a string and concatenating them one to the other, with the provided glue string in between every two elements.

When called on a tuple and with stricter item types (union of literal values, the result is strictly typed to the tuples shape and it's item types).

Data First

R.join(data, glue);
Parameters
data
The array to join.
glue
The string to put in between every two elements.
Returns
Object
R.join([1, 2, 3], ","); // => "1,2,3" (typed `string`)
R.join(["a", "b", "c"], ""); // => "abc" (typed `string`)
R.join(["hello", "world"] as const, " "); // => "hello world" (typed `hello world`)

Data Last

R.join(glue)(data);
Parameters
glue
The string to put in between every two elements.
Returns
Object
R.pipe([1, 2, 3], R.join(",")); // => "1,2,3" (typed `string`)
R.pipe(["a", "b", "c"], R.join("")); // => "abc" (typed `string`)
R.pipe(["hello", "world"] as const, R.join(" ")); // => "hello world" (typed `hello world`)

last

Lazy

Gets the last element of array.

Data First

R.last(array);
Parameters
data
The array.
Returns
Object
R.last([1, 2, 3]); // => 3
R.last([]); // => undefined

Data Last

R.last()(array);
Parameters
Returns
Object
R.pipe(
  [1, 2, 4, 8, 16],
  R.filter((x) => x > 3),
  R.last(),
  (x) => x + 1,
); // => 17

length

Counts values of the collection or iterable.

Data First

R.length(array);
Parameters
items
The input data.
Returns
number
R.length([1, 2, 3]); // => 3

Data Last

R.length()(array);
Parameters
Returns
Object
R.pipe([1, 2, 3], R.length()); // => 3

map

StrictIndexedLazy

Map each element of an array using a defined callback function. If the input array is a tuple use the strict variant to maintain it's shape.

Breaking changes in v2

Typing

Strict

If you weren’t using the strict variant, the return type now maintains the shape of the input array (e.g. if the input was non-empty, the result would also be non-empty). For regular arrays this would result in the same return type. To get the previous return type cast the input tuple as an array of the item type. If you are already using strict simply remove the .strict suffix.

Indexed

The indexed variant was removed; the base implementation takes the same parameters. If you are using indexed you can simply remove it without any other changes.

Runtime

The mapper now takes 2 additional parameters: index - The index of the current element being processed in array, and data - the array the function was called upon (the same signature as the built-in Array.prototype.map).

If you are using a function reference for the predicate (and not an inline arrow function), and that function accepts more than one param you might run into compile-time (or run-time!) issues because of the extra params being sent on each invocation of the function. We highly recommend using unicorn/no-array-callback-reference to warn against these issues.

Examples
Strict variant removed
// Was
map.strict(array, mapper);

// Now
map(array, mapper);
Indexed variant removed
// Was
map.indexed(array, mapper);

// Now
map(array, mapper);
Better typing
const DATA = ["hello"] as [string, ...string[]];

const mapped = map(DATA, (item) => item.length);
//    ^? [number, ...number[]], Was: number[]
Legacy typing
const DATA = ["hello"] as [string, ...string[]];

const mapped = map(DATA as string[], (item) => item.length);
//    ^? number[]
Potential bug
const DATA = ["1", "2", "3"];

// Bug
map(DATA, parseInt); // => [1, NaN, NaN], Was: [1, 2, 3]

// Fix
map(DATA, (raw) => parseInt(raw)); // => [1, 2, 3]

Data First

R.map(array, fn);
Parameters
array
The array to map.
fn
The function mapper.
Returns
Array
R.map([1, 2, 3], (x) => x * 2); // => [2, 4, 6], typed number[]
R.map.indexed([0, 0, 0], (x, i) => i); // => [0, 1, 2], typed number[]
R.map.strict([0, 0] as const, (x) => x + 1); // => [1, 1], typed [number, number]
R.map.strict.indexed([0, 0] as const, (x, i) => x + i); // => [0, 1], typed [number, number]

Data Last

R.map(fn)(array);
Parameters
fn
The function mapper.
Returns
Object
R.pipe(
  [0, 1, 2],
  R.map((x) => x * 2),
); // => [0, 2, 4]
R.pipe(
  [0, 0, 0],
  R.map.indexed((x, i) => i),
); // => [0, 1, 2]

mapToObj

Indexed

Map each element of an array into an object using a defined callback function.

There are several other functions that could be used to build an object from an array:

  • fromKeys - Builds an object from an array of keys and a mapper for values.
  • indexBy - Builds an object from an array of values and a mapper for keys.
  • pullObject - Builds an object from an array of items with mappers for both keys and values.
  • fromEntries - Builds an object from an array of key-value pairs. Refer to the docs for more details.

Data First

R.mapToObj(array, fn);
Parameters
array
The array to map.
fn
The mapping function, which should return a tuple of [key, value], similar to Object.fromEntries.
Returns
Object
R.mapToObj([1, 2, 3], (x) => [String(x), x * 2]); // => {1: 2, 2: 4, 3: 6}
R.mapToObj.indexed([0, 0, 0], (x, i) => [i, i]); // => {0: 0, 1: 1, 2: 2}

Data Last

R.mapToObj(fn)(array);
Parameters
fn
The mapping function, which should return a tuple of [key, value], similar to Object.fromEntries.
Returns
Object
R.pipe(
  [1, 2, 3],
  R.mapToObj((x) => [String(x), x * 2]),
); // => {1: 2, 2: 4, 3: 6}
R.pipe(
  [0, 0, 0],
  R.mapToObj.indexed((x, i) => [i, i]),
); // => {0: 0, 1: 1, 2: 2}

mapWithFeedback

IndexedLazy

Applies a function on each element of the array, using the result of the previous application, and returns an array of the successively computed values.

Data First

R.mapWithFeedback(items, fn, initialValue);
Parameters
array
The array to map over.
reducer
The callback function that receives the previous value, the current element, and optionally the index and the whole array.
initialValue
The initial value to start the computation with.
Returns
Object
R.mapWithFeedback([1, 2, 3, 4, 5], (prev, x) => prev + x, 100); // => [101, 103, 106, 110, 115]
R.mapWithFeedback.indexed(
  [1, 2, 3, 4, 5],
  (prev, x, i, array) => prev + x,
  100,
); // => [101, 103, 106, 110, 115]

Data Last

R.mapWithFeedback(fn, initialValue)(array);
Parameters
reducer
The callback function that receives the previous value, the current element, and optionally the index and the whole array.
initialValue
The initial value to start the computation with.
Returns
Object
R.pipe(
  [1, 2, 3, 4, 5],
  R.mapWithFeedback((prev, x) => prev + x, 100),
); // => [101, 103, 106, 110, 115]
R.pipe(
  [1, 2, 3, 4, 5],
  R.mapWithFeedback.indexed((prev, x, i, array) => prev + x, 100),
); // => [101, 103, 106, 110, 115]

meanBy

Indexed

Returns the mean of the elements of an array using the provided predicate.

Data Last

R.meanBy(fn)(array);
Parameters
fn
Predicate function.
Returns
Object
R.pipe(
  [{ a: 5 }, { a: 1 }, { a: 3 }],
  R.meanBy((x) => x.a),
); // 3

Data First

R.meanBy(array, fn);
Parameters
items
The array.
fn
Predicate function.
Returns
number
R.meanBy([{ a: 5 }, { a: 1 }, { a: 3 }], (x) => x.a); // 3

mergeAll

Merges a list of objects into a single object.

R.mergeAll(objects);
Parameters
array
The array of objects.
Returns
Object
R.mergeAll([{ a: 1, b: 1 }, { b: 2, c: 3 }, { d: 10 }]); // => { a: 1, b: 2, c: 3, d: 10 }

nthBy

Retrieves the element that would be at the given index if the array were sorted according to specified rules. This function uses the QuickSelect algorithm running at an average complexity of O(n). Semantically it is equivalent to sortBy(data, ...rules).at(index) which would run at O(nlogn).

See also firstBy which provides an even more efficient algorithm and a stricter return type, but only for index === 0. See takeFirstBy to get all the elements up to and including index.

Data First

R.nthBy(data, index, ...rules);
Parameters
data
The input array.
index
The zero-based index for selecting the element in the sorted order. Negative indices count backwards from the end.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
R.nthBy([2, 1, 4, 5, 3], 2, identity); // => 3

Data Last

R.nthBy(index, ...rules)(data);
Parameters
index
The zero-based index for selecting the element in the sorted order. Negative indices count backwards from the end.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
R.pipe([2, 1, 4, 5, 3], R.nthBy(2, identity)); // => 3

only

Lazy

Returns the first and only element of array, or undefined otherwise.

Data First

R.only(array);
Parameters
array
The target array.
Returns
Object
R.only([]); // => undefined
R.only([1]); // => 1
R.only([1, 2]); // => undefined

Data Last

R.only()(array);
Parameters
Returns
Object
R.pipe([], R.only()); // => undefined
R.pipe([1], R.only()); // => 1
R.pipe([1, 2], R.only()); // => undefined

partition

Indexed

Splits a collection into two groups, the first of which contains elements the predicate type guard passes, and the second one containing the rest.

Breaking changes in v2

Typing

The indexed variant was removed; the base implementation takes the same parameters. If you are using indexed you can simply remove it without any other changes.

Runtime

The predicate function now takes 2 additional parameters: index - The index of the current element being processed in array, and data - the array the function was called upon (the same signature the callbacks the built-in Array.prototype functions have).

If you are using a function reference for the predicate (and not an inline arrow function), and that function accepts more than one param you might run into compile-time (or run-time!) issues because of the extra params being sent on each invocation of the function. We highly recommend using unicorn/no-array-callback-reference to warn against these issues.

Examples
Indexed variant removed
// Was
partition.indexed(array, predicate);

// Now
partition(array, predicate);
Potential bug
function callback(value: number, index = 0): boolean {
  return (value + index) % 2 === 0;
}

// Bug
partition([1, 2, 3], callback); // => [[], [1, 2, 3]], Was: [[2], [1, 3]]

// Fix
partition([1, 2, 3], (item) => callback(item)); // => [[2], [1, 3]]

Data First

R.partition(array, fn);
Parameters
items
The items to split.
predicate
A type guard function to invoke on every item.
Returns
Object
R.partition(["one", "two", "forty two"], (x) => x.length === 3); // => [['one', 'two'], ['forty two']]

Data First

R.partition(array, fn);
Parameters
items
The items to split.
predicate
The function invoked per iteration.
Returns
Object
R.partition(["one", "two", "forty two"], (x) => x.length === 3); // => [['one', 'two'], ['forty two']]

Data Last

R.partition(fn)(array);
Parameters
predicate
The grouping function.
Returns
Object
R.pipe(
  ["one", "two", "forty two"],
  R.partition((x) => x.length === 3),
); // => [['one', 'two'], ['forty two']]

Data Last

R.partition(fn)(array);
Parameters
predicate
The grouping function.
Returns
Object
R.pipe(
  ["one", "two", "forty two"],
  R.partition((x) => x.length === 3),
); // => [['one', 'two'], ['forty two']]

range

Returns a list of numbers from start (inclusive) to end (exclusive).

Data First

range(start, end);
Parameters
start
The start number.
end
The end number.
Returns
Array
R.range(1, 5); // => [1, 2, 3, 4]

Data First

range(end)(start);
Parameters
end
The end number.
Returns
Object
R.range(5)(1); // => [1, 2, 3, 4]

rankBy

Calculates the rank of an item in an array based on rules. The rank is the position where the item would appear in the sorted array. This function provides an efficient way to determine the rank in O(n) time, compared to O(nlogn) for the equivalent sortedIndex(sortBy(data, ...rules), item).

Data First

R.rankBy(data, item, ...rules);
Parameters
data
The input array.
item
The item whose rank is to be determined.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
number
const DATA = [{ a: 5 }, { a: 1 }, { a: 3 }] as const;
R.rankBy(DATA, 0, R.prop("a")); // => 0
R.rankBy(DATA, 1, R.prop("a")); // => 1
R.rankBy(DATA, 2, R.prop("a")); // => 1
R.rankBy(DATA, 3, R.prop("a")); // => 2

Data Last

R.rankBy(item, ...rules)(data);
Parameters
item
The item whose rank is to be determined.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
const DATA = [{ a: 5 }, { a: 1 }, { a: 3 }] as const;
R.pipe(DATA, R.rankBy(0, R.prop("a"))); // => 0
R.pipe(DATA, R.rankBy(1, R.prop("a"))); // => 1
R.pipe(DATA, R.rankBy(2, R.prop("a"))); // => 1
R.pipe(DATA, R.rankBy(3, R.prop("a"))); // => 2

reduce

Indexed

Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.

Breaking changes in v2

Typing

The indexed variant was removed; the base implementation takes the same parameters. If you are using indexed you can simply remove it without any other changes.

Runtime

The predicate function now takes 2 additional parameters: index - The index of the current element being processed in array, and data - the array the function was called upon (the same signature as the built-in Array.prototype.reduce).

If you are using a function reference for the predicate (and not an inline arrow function), and that function accepts more than one param you might run into compile-time (or run-time!) issues because of the extra params being sent on each invocation of the function. We highly recommend using unicorn/no-array-callback-reference to warn against these issues.

Examples
Indexed variant removed
// Was
reduce.indexed([1, 2, 3], (acc, item, index) => acc + item + index, 0);

// Now
reduce([1, 2, 3], (acc, item, index) => acc + item + index, 0);
Potential bug
function callback(acc: number, item: number, index = 0): number {
  return acc + item + index;
}

// Bug
reduce([1, 2, 3], callback, 0); // => 9, Was: 6

// Fix
reduce([1, 2, 3], (acc, item) => callback(acc, item), 0); // => 6

Data First

R.reduce(items, fn, initialValue);
Parameters
items
The array to reduce.
fn
The callback function.
initialValue
The initial value to use as an accumulator value in the callback function.
Returns
Object
R.reduce([1, 2, 3, 4, 5], (acc, x) => acc + x, 100); // => 115
R.reduce.indexed([1, 2, 3, 4, 5], (acc, x, i, array) => acc + x, 100); // => 115

Data Last

R.reduce(fn, initialValue)(array);
Parameters
fn
The callback function.
initialValue
The initial value to use as an accumulator value in the callback function.
Returns
Object
R.pipe(
  [1, 2, 3, 4, 5],
  R.reduce((acc, x) => acc + x, 100),
); // => 115
R.pipe(
  [1, 2, 3, 4, 5],
  R.reduce.indexed((acc, x, i, array) => acc + x, 100),
); // => 115

reverse

Reverses array.

Data First

R.reverse(arr);
Parameters
array
The array.
Returns
Object
R.reverse([1, 2, 3]); // [3, 2, 1]

Data Last

R.reverse()(array);
Parameters
Returns
Object
R.reverse()([1, 2, 3]); // [3, 2, 1]

sample

Lazy

Returns a random subset of size sampleSize from array.

Maintains and infers most of the typing information that could be passed along to the output. This means that when using tuples, the output will be a tuple too, and when using literals, those literals would be preserved.

The items in the result are kept in the same order as they are in the input. If you need to get a shuffled response you can pipe the shuffle function after this one.

Breaking changes in v2

Runtime

No longer throws negative sample sizes or non-integer sample sizes, instead returns an empty result. This aligns better with how the rest of the library functions, and with how built-in functions handle these situations (e.g. slice(1.5))

Examples
Fails silently
// No longer throws:
sample([1, 2, 3], -1); // => []

// No longer throws:
sample([1, 2, 3], 2.5); // => []

Data First

R.sample(array, sampleSize);
Parameters
data
The array.
sampleSize
The number of elements to take.
Returns
Object
R.sample(["hello", "world"], 1); // => ["hello"] // typed string[]
R.sample(["hello", "world"] as const, 1); // => ["world"] // typed ["hello" | "world"]

Data Last

R.sample(sampleSize)(array);
Parameters
sampleSize
The number of elements to take.
Returns
Object
R.sample(1)(["hello", "world"]); // => ["hello"] // typed string[]
R.sample(1)(["hello", "world"] as const); // => ["world"] // typed ["hello" | "world"]

shuffle

Shuffles the input array, returning a new array with the same elements in a random order.

Data First

R.shuffle(array);
Parameters
items
The array to shuffle.
Returns
Array
R.shuffle([4, 2, 7, 5]); // => [7, 5, 4, 2]

Data Last

R.shuffle()(array);
Parameters
Returns
Object
R.pipe([4, 2, 7, 5], R.shuffle()); // => [7, 5, 4, 2]

sort

Strict

Sorts an array. The comparator function should accept two values at a time and return a negative number if the first value is smaller, a positive number if it's larger, and zero if they are equal. Sorting is based on a native sort function. It's not guaranteed to be stable.

If the input array is more complex (non-empty array, tuple, etc...) use the strict mode to maintain its shape.

Breaking changes in v2

Typing

If you weren’t using the strict variant, the return type is now stricter when called on a tuple; instead of just an array, the shape of the tuple would be mirrored in the output (so if your input is a non-empty array, your output would also be a non-empty array). If you are already using strict simply remove the .strict suffix.

Examples
Strict variant removed
// Was
sort.strict([1, 2, 3], (a, b) => a - b);

// Now
sort([1, 2, 3], (a, b) => a - b);
Preserves input shape
const DATA = [1, true, "hello"] as [number, boolean, ...string[]];

const result = sort(DATA, (a, b) => a - b);
//    ^? [number | boolean | string, number | boolean | string, ...(number | boolean | string)[]]
//  Was: (number | boolean | string)[]

Data First

R.sort(items, cmp);
Parameters
items
The array to sort.
cmp
The comparator function.
Returns
Array
R.sort([4, 2, 7, 5], (a, b) => a - b); // => [2, 4, 5, 7] typed Array<number>
R.sort.strict([4, 2] as [number, number], (a, b) => a - b); // [2, 4] typed [number, number]

Data Last

R.sort(cmp)(items);
Parameters
cmp
The comparator function.
Returns
Object
R.pipe(
  [4, 2, 7, 5],
  R.sort((a, b) => a - b),
); // => [2, 4, 5, 7] typed Array<number>
R.pipe(
  [4, 2] as [number, number],
  R.sort.strict((a, b) => a - b),
); // => [2, 4] typed [number, number]

sortBy

Strict

Sorts data using the provided ordering rules. The sort is done via the native Array.prototype.sort but is performed on a shallow copy of the array to avoid mutating the original data.

To maintain the shape of more complex inputs (like non-empty arrays, tuples, etc...) use the strict variant.

There are several other functions that take order rules and bypass the need to sort the array first (in O(nlogn) time):

  • firstBy === first(sortBy(data, ...rules)), O(n).
  • takeFirstBy === take(sortBy(data, ...rules), k), O(nlogk).
  • dropFirstBy === drop(sortBy(data, ...rules), k), O(nlogk).
  • nthBy === sortBy(data, ...rules).at(k), O(n).
  • rankBy === sortedIndex(sortBy(data, ...rules), item), O(n). Refer to the docs for more details.

Data Last

R.sortBy(...rules)(data);
Parameters
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
R.pipe(
  [{ a: 1 }, { a: 3 }, { a: 7 }, { a: 2 }],
  R.sortBy((x) => x.a),
); // => [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 7 }] typed Array<{a:number}>
R.pipe(
  [{ a: 1 }, { a: 3 }] as const,
  R.sortBy.strict((x) => x.a),
); // => [{ a: 1 }, { a: 3 }] typed [{a: 1 | 3}, {a: 1 | 3}]

Data First

R.sortBy(data, ...rules);
Parameters
array
The input array.
sortRules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Array
R.sortBy([{ a: 1 }, { a: 3 }, { a: 7 }, { a: 2 }], (x) => x.a);
// => [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 7 }] typed Array<{a:number}>

R.sortBy(
  [
    { color: "red", weight: 2 },
    { color: "blue", weight: 3 },
    { color: "green", weight: 1 },
    { color: "purple", weight: 1 },
  ],
  [(x) => x.weight, "asc"],
  (x) => x.color,
);
// =>
//   {color: 'green', weight: 1},
//   {color: 'purple', weight: 1},
//   {color: 'red', weight: 2},
//   {color: 'blue', weight: 3},
// typed Array<{color: string, weight: number}>

R.sortBy.strict([{ a: 1 }, { a: 3 }] as const, (x) => x.a);
// => [{ a: 1 }, { a: 3 }] typed [{a: 1 | 3}, {a: 1 | 3}]

sortedIndex

Find the insertion position (index) of an item in an array with items sorted in ascending order; so that splice(sortedIndex, 0, item) would result in maintaining the array's sort-ness. The array can contain duplicates. If the item already exists in the array the index would be of the first occurrence of the item.

Runs in O(logN) time.

Data First

R.sortedIndex(data, item);
Parameters
data
The (ascending) sorted array.
item
The item to insert.
Returns
number
R.sortedIndex(["a", "a", "b", "c", "c"], "c"); // => 3

Data Last

R.sortedIndex(item)(data);
Parameters
item
The item to insert.
Returns
Object
R.pipe(["a", "a", "b", "c", "c"], R.sortedIndex("c")); // => 3

sortedIndexBy

Indexed

Find the insertion position (index) of an item in an array with items sorted in ascending order using a value function; so that splice(sortedIndex, 0, item) would result in maintaining the arrays sort-ness. The array can contain duplicates. If the item already exists in the array the index would be of the first occurrence of the item.

Runs in O(logN) time.

Data First

R.sortedIndexBy(data, item, valueFunction);
Parameters
data
The (ascending) sorted array.
item
The item to insert.
valueFunction
All comparisons would be performed on the result of calling this function on each compared item. Preferably this function should return a `number` or `string`. This function should be the same as the one provided to sortBy to sort the array.
Returns
number
R.sortedIndexBy([{ age: 20 }, { age: 22 }], { age: 21 }, prop("age")); // => 1

Data Last

R.sortedIndexBy(data, item, valueFunction);
Parameters
item
The item to insert.
valueFunction
All comparisons would be performed on the result of calling this function on each compared item. Preferably this function should return a `number` or `string`. This function should be the same as the one provided to sortBy to sort the array.
Returns
Object
R.sortedIndexBy([{ age: 20 }, { age: 22 }], { age: 21 }, prop("age")); // => 1

sortedIndexWith

Indexed

Performs a binary search for the index of the item at which the predicate stops returning true. This function assumes that the array is "sorted" in regards to the predicate, meaning that running the predicate as a mapper on it would result in an array [...true[], ...false[]]. This stricter requirement from the predicate provides us 2 benefits over findIndex which does a similar thing:

  1. It would run at O(logN) time instead of O(N) time.
  2. It always returns a value (it would return data.length if the predicate returns true for all items).

This function is the basis for all other sortedIndex functions which search for a specific item in a sorted array, and it could be used to perform similar efficient searches.

Data First

R.sortedIndexWith(data, predicate);
Parameters
data
Array, "sorted" by `predicate`.
predicate
A predicate which also defines the array's order.
Returns
number
R.sortedIndexWith(["a", "ab", "abc"], (item) => item.length < 2); // => 1

Data Last

R.sortedIndexWith(predicate)(data);
Parameters
predicate
A predicate which also defines the array's order.
Returns
Object
R.pipe(
  ["a", "ab", "abc"],
  R.sortedIndexWith((item) => item.length < 2),
); // => 1

sortedLastIndex

Find the insertion position (index) of an item in an array with items sorted in ascending order; so that splice(sortedIndex, 0, item) would result in maintaining the array's sort-ness. The array can contain duplicates. If the item already exists in the array the index would be of the last occurrence of the item.

Runs in O(logN) time.

Data First

R.sortedLastIndex(data, item);
Parameters
data
The (ascending) sorted array.
item
The item to insert.
Returns
number
R.sortedLastIndex(["a", "a", "b", "c", "c"], "c"); // => 5

Data Last

R.sortedLastIndex(item)(data);
Parameters
item
The item to insert.
Returns
Object
R.pipe(["a", "a", "b", "c", "c"], sortedLastIndex("c")); // => 5

sortedLastIndexBy

Indexed

Find the insertion position (index) of an item in an array with items sorted in ascending order using a value function; so that splice(sortedIndex, 0, item) would result in maintaining the arrays sort-ness. The array can contain duplicates. If the item already exists in the array the index would be of the last occurrence of the item.

Runs in O(logN) time.

Data First

R.sortedLastIndexBy(data, item, valueFunction);
Parameters
data
The (ascending) sorted array.
item
The item to insert.
valueFunction
All comparisons would be performed on the result of calling this function on each compared item. Preferably this function should return a `number` or `string`. This function should be the same as the one provided to sortBy to sort the array.
Returns
number
R.sortedLastIndexBy([{ age: 20 }, { age: 22 }], { age: 21 }, prop("age")); // => 1

Data Last

R.sortedLastIndexBy(item, valueFunction)(data);
Parameters
item
The item to insert.
valueFunction
All comparisons would be performed on the result of calling this function on each compared item. Preferably this function should return a `number` or `string`. This function should be the same as the one provided to sortBy to sort the array.
Returns
Object
R.pipe([{ age: 20 }, { age: 22 }], sortedLastIndexBy({ age: 21 }, prop("age"))); // => 1

splice

Removes elements from an array and, inserts new elements in their place.

Data First

R.splice(items, start, deleteCount, replacement);
Parameters
items
The array to splice.
start
The index from which to start removing elements.
deleteCount
The number of elements to remove.
replacement
The elements to insert into the array in place of the deleted elements.
Returns
Array
R.splice([1, 2, 3, 4, 5, 6, 7, 8], 2, 3, []); //=> [1,2,6,7,8]
R.splice([1, 2, 3, 4, 5, 6, 7, 8], 2, 3, [9, 10]); //=> [1,2,9,10,6,7,8]

Data Last

R.splice(start, deleteCount, replacement)(items);
Parameters
start
The index from which to start removing elements.
deleteCount
The number of elements to remove.
replacement
The elements to insert into the array in place of the deleted elements.
Returns
Object
R.pipe([1, 2, 3, 4, 5, 6, 7, 8], R.splice(2, 3, [])); // => [1,2,6,7,8]
R.pipe([1, 2, 3, 4, 5, 6, 7, 8], R.splice(2, 3, [9, 10])); // => [1,2,9,10,6,7,8]

splitAt

Splits a given array at a given index.

Data First

R.splitAt(array, index);
Parameters
array
The array to split.
index
The index to split at.
Returns
Object
R.splitAt([1, 2, 3], 1); // => [[1], [2, 3]]
R.splitAt([1, 2, 3, 4, 5], -1); // => [[1, 2, 3, 4], [5]]

Data Last

R.splitAt(index)(array);
Parameters
index
The index to split at.
Returns
Object
R.splitAt(1)([1, 2, 3]); // => [[1], [2, 3]]
R.splitAt(-1)([1, 2, 3, 4, 5]); // => [[1, 2, 3, 4], [5]]

splitWhen

Splits a given array at the first index where the given predicate returns true.

Data First

R.splitWhen(array, fn);
Parameters
array
The array to split.
fn
The predicate.
Returns
Object
R.splitWhen([1, 2, 3], (x) => x === 2); // => [[1], [2, 3]]

Data Last

R.splitWhen(fn)(array);
Parameters
fn
The predicate.
Returns
Object
R.splitWhen((x) => x === 2)([1, 2, 3]); // => [[1], [2, 3]]

sumBy

Indexed

Returns the sum of the elements of an array using the provided predicate.

Data Last

R.sumBy(fn)(array);
Parameters
fn
Predicate function.
Returns
Object
R.pipe(
  [{ a: 5 }, { a: 1 }, { a: 3 }],
  R.sumBy((x) => x.a),
); // 9

Data First

R.sumBy(array, fn);
Parameters
items
The array.
fn
Predicate function.
Returns
number
R.sumBy([{ a: 5 }, { a: 1 }, { a: 3 }], (x) => x.a); // 9

swapIndices

Swaps the positions of two elements in an array or string at the provided indices.

Negative indices are supported and would be treated as an offset from the end of the array. The resulting type thought would be less strict than when using positive indices.

If either index is out of bounds the result would be a shallow copy of the input, as-is.

Data First

swapIndices(data, index1, index2);
Parameters
data
The item to be manipulated. This can be an array, or a string.
index1
The first index.
index2
The second index.
Returns
Object
swapIndices(["a", "b", "c"], 0, 1); // => ['b', 'a', 'c']
swapIndices(["a", "b", "c"], 1, -1); // => ['c', 'b', 'a']
swapIndices("abc", 0, 1); // => 'bac'

Data Last

swapIndices(index1, index2)(data);
Parameters
index1
The first index.
index2
The second index.
Returns
Object
swapIndices(0, 1)(["a", "b", "c"]); // => ['b', 'a', 'c']
swapIndices(0, -1)("abc"); // => 'cba'

take

Lazy

Returns the first n elements of array.

Data First

R.take(array, n);
Parameters
array
The array.
n
The number of elements to take.
Returns
Array
R.take([1, 2, 3, 4, 3, 2, 1], 3); // => [1, 2, 3]

Data Last

R.take(n)(array);
Parameters
n
The number of elements to take.
Returns
Object
R.pipe([1, 2, 3, 4, 3, 2, 1], R.take(n)); // => [1, 2, 3]

takeFirstBy

Take the first n items from data based on the provided ordering criteria. This allows you to avoid sorting the array before taking the items. The complexity of this function is O(Nlogn) where N is the length of the array.

For the opposite operation (to drop n elements) see dropFirstBy.

Data First

R.takeFirstBy(data, n, ...rules);
Parameters
data
The input array.
n
The number of items to take. If `n` is non-positive no items would be returned, if `n` is bigger then data.length a *clone* of `data` would be returned.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Array
R.takeFirstBy(["aa", "aaaa", "a", "aaa"], 2, (x) => x.length); // => ['a', 'aa']

Data Last

R.takeFirstBy(n, ...rules)(data);
Parameters
n
The number of items to take. If `n` is non-positive no items would be returned, if `n` is bigger then data.length a *clone* of `data` would be returned.
rules
A variadic array of order rules defining the sorting criteria. Each order rule is a projection function that extracts a comparable value from the data. Sorting is based on these extracted values using the native `<` and `>` operators. Earlier rules take precedence over later ones. Use the syntax `[projection, "desc"]` for descending order.
Returns
Object
R.pipe(
  ["aa", "aaaa", "a", "aaa"],
  R.takeFirstBy(2, (x) => x.length),
); // => ['a', 'aa']

takeLastWhile

Returns elements from the end of the array until the predicate returns false. The returned elements will be in the same order as in the original array.

Data First

R.takeLastWhile(data, predicate);
Parameters
data
The array.
predicate
The predicate.
Returns
Array
R.takeLastWhile([1, 2, 10, 3, 4, 5], (x) => x < 10); // => [3, 4, 5]

Data Last

R.takeLastWhile(predicate)(data);
Parameters
predicate
The predicate.
Returns
Object
R.pipe(
  [1, 2, 10, 3, 4, 5],
  R.takeLastWhile((x) => x < 10),
); // => [3, 4, 5]

takeWhile

Returns elements from the array until predicate returns false.

Data First

R.takeWhile(array, fn);
Parameters
array
The array.
fn
The predicate.
Returns
Array
R.takeWhile([1, 2, 3, 4, 3, 2, 1], (x) => x !== 4); // => [1, 2, 3]

Data Last

R.takeWhile(fn)(array);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 2, 3, 4, 3, 2, 1],
  R.takeWhile((x) => x !== 4),
); // => [1, 2, 3]

unique

Lazy

Returns a new array containing only one copy of each element in the original list. Elements are compared by reference using Set.

Data First

R.unique(array);
Parameters
array
The array to filter.
Returns
Array
R.unique([1, 2, 2, 5, 1, 6, 7]); // => [1, 2, 5, 6, 7]

Data Last

R.unique()(array);
Parameters
Returns
Object
R.pipe(
  [1, 2, 2, 5, 1, 6, 7], // only 4 iterations
  R.unique(),
  R.take(3),
); // => [1, 2, 5]

uniqueBy

Lazy

Returns a new array containing only one copy of each element in the original list transformed by a function. Elements are compared by reference using Set.

Data First

R.uniqueBy(array, fn);
Parameters
data
The array to filter.
keyFunction
Extracts a value that would be used to compare elements.
Returns
Array
R.uniqueBy(
  [{ n: 1 }, { n: 2 }, { n: 2 }, { n: 5 }, { n: 1 }, { n: 6 }, { n: 7 }],
  (obj) => obj.n,
); // => [{n: 1}, {n: 2}, {n: 5}, {n: 6}, {n: 7}]

Data Last

R.uniqueBy(fn)(array);
Parameters
keyFunction
Extracts a value that would be used to compare elements.
Returns
Object
R.pipe(
  [{ n: 1 }, { n: 2 }, { n: 2 }, { n: 5 }, { n: 1 }, { n: 6 }, { n: 7 }], // only 4 iterations
  R.uniqueBy((obj) => obj.n),
  R.take(3),
); // => [{n: 1}, {n: 2}, {n: 5}]

uniqueWith

Returns a new array containing only one copy of each element in the original list. Elements are compared by custom comparator isEquals.

Data First

R.uniqueWith(array, isEquals);
Parameters
array
The array to filter.
isEquals
The comparator.
Returns
Array
R.uniqueWith(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }],
  R.equals,
); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]

Data Last

R.uniqueWith(isEquals)(array);
Parameters
isEquals
The comparator.
Returns
Object
R.uniqueWith(R.equals)([
  { a: 1 },
  { a: 2 },
  { a: 2 },
  { a: 5 },
  { a: 1 },
  { a: 6 },
  { a: 7 },
]); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]
R.pipe(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }], // only 4 iterations
  R.uniqueWith(R.equals),
  R.take(3),
); // => [{a: 1}, {a: 2}, {a: 5}]

zip

Strict

Creates a new list from two supplied lists by pairing up equally-positioned items. The length of the returned list will match the shortest of the two inputs.

If the input array are tuples, you can use the strict option to get another tuple instead of a generic array type.

Breaking changes in v2

Typing

If you weren’t using the strict variant, The returned type is now stricter by taking the positional type of each item in tuples correctly, allowing the types themselves to be zipped together, creating a combined tuple as output. If you are zipping simple arrays the result is unchanged. If you are already using the strict variant simply remove the .strict suffix.

Examples
Strict
// Was
zip.strict(a, b);

// Now
zip(a, b);
Better typing
const A = [1, 2, 3] as const;
const B = ["a", "b", "c"] as const;

// Was
const result = zip(A, B);
//    ^? [1 | 2 | 3, "a" | "b" | "c"][]

// Now
const result = zip(A, B);
//    ^? [[1, "a"], [2, "b"], [3, "c"]]

Data First

R.zip(first, second);
Parameters
first
The first input list.
second
The second input list.
Returns
Array
R.zip([1, 2], ["a", "b"]); // => [[1, 'a'], [2, 'b']] (type: [number, string][])
R.zip.strict([1, 2] as const, ["a", "b"] as const); // => [[1, 'a'], [2, 'b']]  (type: [[1, 'a'], [2, 'b']])

Data Last

R.zip(second)(first);
Parameters
second
The second input list.
Returns
Object
R.zip(["a", "b"])([1, 2]); // => [[1, 'a'], [2, 'b']] (type: [number, string][])
R.zip.strict(["a", "b"] as const)([1, 2] as const); // => [[1, 'a'], [2, 'b']]  (type: [[1, 'a'], [2, 'b']])

zipWith

Creates a new list from two supplied lists by calling the supplied function with the same-positioned element from each list.

Data First

R.zipWith(first, second, fn);
Parameters
first
The first input list.
second
The second input list.
fn
The function applied to each position of the list.
Returns
Array
R.zipWith(["1", "2", "3"], ["a", "b", "c"], (a, b) => a + b); // => ['1a', '2b', '3c']

Data Last

R.zipWith(fn)(first, second);
Parameters
fn
The function applied to each position of the list.
Returns
Object
R.zipWith((a, b) => a + b)(["1", "2", "3"], ["a", "b", "c"]); // => ['1a', '2b', '3c']

Data Last

R.zipWith(fn)(first, second);
Parameters
fn
The function applied to each position of the list.
second
The second input list.
Returns
Object
R.zipWith((a, b) => a + b, ["a", "b", "c"])(["1", "2", "3"]); // => ['1a', '2b', '3c']

Function

conditional

Executes a transformer function based on the first matching predicate, functioning like a series of if...else if... statements. It sequentially evaluates each case and, upon finding a truthy predicate, runs the corresponding transformer, and returns, ignoring any further cases, even if they would match.

!IMPORTANT! - Unlike similar implementations in frameworks like Lodash and Ramda, the Remeda implementation does NOT return a default/fallback undefined value when none of the cases match; and instead will throw an exception in those cases. To add a default case use the conditional.defaultCase helper as the final case of your implementation. By default it returns undefined, but could be provided a transformer in order to return something else.

Due to TypeScript's inability to infer the result of negating a type- predicate we can't refine the types used in subsequent cases based on previous conditions. Using a switch (true) statement or ternary operators is recommended for more precise type control when such type narrowing is needed.

Data Last

R.conditional(...cases)(data);
Parameters
case0
case1
case2
case3
case4
case5
case6
case7
case8
case9
Returns
Object
const nameOrId = 3 as string | number;
R.pipe(
  nameOrId,
  R.conditional(
    [R.isString, (name) => `Hello ${name}`],
    [R.isNumber, (id) => `Hello ID: ${id}`],
    R.conditional.defaultCase(
      (something) => `Hello something (${JSON.stringify(something)})`,
    ),
  ),
); //=> 'Hello ID: 3'

Data First

R.conditional(data, ...cases);
Parameters
data
The input data to be evaluated against the provided cases.
case0
case1
case2
case3
case4
case5
case6
case7
case8
case9
Returns
Object
const nameOrId = 3 as string | number;
R.conditional(
  nameOrId,
  [R.isString, (name) => `Hello ${name}`],
  [R.isNumber, (id) => `Hello ID: ${id}`],
  R.conditional.defaultCase(
    (something) => `Hello something (${JSON.stringify(something)})`,
  ),
); //=> 'Hello ID: 3'

constant

A function that takes any arguments and returns the provided value on every invocation. This is useful to provide trivial implementations for APIs or in combination with a ternary or other conditional execution to allow to short- circuit more complex implementations for a specific case.

Notice that this is a dataLast impl where the function needs to be invoked to get the "do nothing" function.

Data Last

R.constant(value);
Parameters
value
The constant value that would be returned on every invocation. The value is not copied/cloned on every invocation so care should be taken with mutable objects (like arrays, objects, Maps, etc...).
Returns
Object
R.map([1, 2, 3], R.constant("a")); // => ['a', 'a', 'a']
R.map([1, 2, 3], isDemoMode ? R.add(1) : R.constant(0)); // => [2, 3, 4] or [0, 0, 0]

debounce

Wraps func with a debouncer object that "debounces" (delays) invocations of the function during a defined cool-down period (waitMs). It can be configured to invoke the function either at the start of the cool-down period, the end of it, or at both ends (timing). It can also be configured to allow invocations during the cool-down period (maxWaitMs). It stores the latest call's arguments so they could be used at the end of the cool-down period when invoking func (if configured to invoke the function at the end of the cool-down period). It stores the value returned by func whenever its invoked. This value is returned on every call, and is accessible via the cachedValue property of the debouncer. Its important to note that the value might be different from the value that would be returned from running func with the current arguments as it is a cached value from a previous invocation. Important: The cool-down period defines the minimum between two invocations, and not the maximum. The period will be extended each time a call is made until a full cool-down period has elapsed without any additional calls.

Data First

R.debounce(func, options);
Parameters
func
The function to debounce, the returned `call` function will have the exact same signature.
options
An object allowing further customization of the debouncer: - `timing?: 'leading' | 'trailing' |'both'`. The default is `'trailing'`. `leading` would result in the function being invoked at the start of the cool-down period; `trailing` would result in the function being invoked at the end of the cool-down period (using the args from the last call to the debouncer). When `both` is selected the `trailing` invocation would only take place if there were more than one call to the debouncer during the cool-down period. **DEFAULT: 'trailing'** - `waitMs?: number`. The length of the cool-down period in milliseconds. The debouncer would wait until this amount of time has passed without **any** additional calls to the debouncer before triggering the end-of-cool-down- period event. When this happens, the function would be invoked (if `timing` isn't `'leading'`) and the debouncer state would be reset. **DEFAULT: 0** - `maxWaitMs?: number`. The length of time since a debounced call (a call that the debouncer prevented from being invoked) was made until it would be invoked. Because the debouncer can be continually triggered and thus never reach the end of the cool-down period, this allows the function to still be invoked occasionally. IMPORTANT: This param is ignored when `timing` is `'leading'`.
Returns
Object
const debouncer = debounce(identity, { timing: "trailing", waitMs: 1000 });
const result1 = debouncer.call(1); // => undefined
const result2 = debouncer.call(2); // => undefined
// after 1 second
const result3 = debouncer.call(3); // => 2
// after 1 second
debouncer.cachedValue; // => 3

doNothing

A function that takes any arguments and does nothing with them. This is useful as a placeholder for any function or API that requires a void function (a function that doesn't return a value). This could also be used in combination with a ternary or other conditional execution to allow disabling a function call for a specific case.

Notice that this is a dataLast impl where the function needs to be invoked to get the "do nothing" function.

Data Last

R.doNothing();
Parameters
Returns
Object
myApi({ onSuccess: handleSuccess, onError: R.doNothing() });
myApi({ onSuccess: isDemoMode ? R.doNothing() : handleSuccess });

identity

A function that always returns the param passed to it.

Breaking changes in v2

Typing

The “headless” mode of the function is no longer available. The function now acts as a factory that creates an identity function. To fix headless cases simply add parenthesis to the end: identity -> identity().

Examples
No headless version
// Was
map(DATA, identity);

// Now
map(DATA, identity());
R.identity(data);
Parameters
value
The param to return.
Returns
Object
R.identity("foo"); // => 'foo'

once

Creates a function that is restricted to invoking func once. Repeat calls to the function return the value of the first invocation.

R.once(fn);
Parameters
fn
The function to wrap.
Returns
Object
const initialize = R.once(createApplication);
initialize();
initialize();
// => `createApplication` is invoked once

pipe

Perform left-to-right function composition.

Data First

R.pipe(data, op1, op2, op3);
Parameters
value
The initial value.
op1
Returns
Object
R.pipe(
  [1, 2, 3, 4],
  R.map((x) => x * 2),
  (arr) => [arr[0] + arr[1], arr[2] + arr[3]],
); // => [6, 14]

piped

A dataLast version of pipe that could be used to provide more complex computations to functions that accept a function as a param (like map, filter, groupBy, etc.).

The first function must be always annotated. Other functions are automatically inferred.

R.piped(...ops)(data);
Parameters
op1
Returns
Object
R.filter(
  [{ a: 1 }, { a: 2 }, { a: 3 }],
  R.piped(R.prop("a"), (x) => x % 2 === 0),
); // => [{ a: 2 }]

purry

Creates a function with dataFirst and dataLast signatures.

purry is a dynamic function and it's not type safe. It should be wrapped by a function that have proper typings. Refer to the example below for correct usage.

!IMPORTANT: functions that simply call purry and return the result (like almost all functions in this library) should return unknown themselves if an explicit return type is required. This is because we currently don't provide a generic return type that is built from the input function, and crafting one manually isn't worthwhile as we rely on function declaration overloading to combine the types for dataFirst and dataLast invocations!

Breaking changes in v2

Runtime

The function no longer looks for a lazy prop on the dataFirst function implementation itself, and will only use the lazy impl provided as the third argument to the purry call itself.

Examples
Implicit lazy removed (with Object.assign)
function myFunc(...args: readonly unknown[]): unknown {
  // Was:
  return purry(withLazy, args);

  // Now:
  return purry(dataFirstImpl, args, lazyImpl);
}

// This can be removed now:
function withLazy = Object.assign(dataFirstImpl, { lazy: lazyImpl });

function dataFirstImpl(...) {
  // ...
}

function lazyImpl(...): LazyEvaluator {
  // ...
}
Implicit lazy removed (with a namespace)
function myFunc(...args: readonly unknown[]): unknown {
  // Was:
  return purry(dataFirstImpl, args);

  // Now:
  return purry(dataFirstImpl, args, lazyImpl);
}

function dataFirstImpl(...) {
  // ...
}

// This can be removed now:
namespace dataFirstImpl {
  export const lazy = lazyImpl;
}

function lazyImpl(...): LazyEvaluator {
  // ...
}
R.purry(fn, arguments);
Parameters
fn
The function to purry.
args
The arguments.
lazyFactory
A lazy version of the function to purry.
Returns
unknown
function _findIndex(array, fn) {
  for (let i = 0; i < array.length; i++) {
    if (fn(array[i])) {
      return i;
    }
  }
  return -1;
}

// data-first
function findIndex<T>(array: T[], fn: (item: T) => boolean): number;

// data-last
function findIndex<T>(fn: (item: T) => boolean): (array: T[]) => number;

function findIndex() {
  return R.purry(_findIndex, arguments);
}

Guard

hasSubObject

Checks if subObject is a sub-object of object, which means for every property and value in subObject, there's the same property in object with an equal value. Equality is checked with isDeepEqual.

Data First

R.hasSubObject(data, subObject);
Parameters
data
The object to test.
subObject
The sub-object to test against.
Returns
boolean
R.hasSubObject({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 }); //=> true
R.hasSubObject({ a: 1, b: 2, c: 3 }, { b: 4 }); //=> false
R.hasSubObject({ a: 1, b: 2, c: 3 }, {}); //=> true

Data Last

R.hasSubObject(subObject)(data);
Parameters
subObject
The sub-object to test against.
Returns
Object
R.hasSubObject({ a: 1, c: 3 })({ a: 1, b: 2, c: 3 }); //=> true
R.hasSubObject({ b: 4 })({ a: 1, b: 2, c: 3 }); //=> false
R.hasSubObject({})({ a: 1, b: 2, c: 3 }); //=> true

isArray

A function that checks if the passed parameter is an Array and narrows its type accordingly.

R.isArray(data);
Parameters
data
The variable to check.
Returns
boolean
R.isArray([5]); //=> true
R.isArray([]); //=> true
R.isArray("somethingElse"); //=> false

isBoolean

A function that checks if the passed parameter is a boolean and narrows its type accordingly.

R.isBoolean(data);
Parameters
data
The variable to check.
Returns
boolean
R.isBoolean(true); //=> true
R.isBoolean(false); //=> true
R.isBoolean("somethingElse"); //=> false

isDate

A function that checks if the passed parameter is a Date and narrows its type accordingly.

R.isDate(data);
Parameters
data
The variable to check.
Returns
boolean
R.isDate(new Date()); //=> true
R.isDate("somethingElse"); //=> false

isDeepEqual

Performs a deep semantic comparison between two values to determine if they are equivalent. For primitive values this is equivalent to ===, for arrays the check would be performed on every item recursively, in order, and for objects all props will be compared recursively. The built-in Date and RegExp are special-cased and will be compared by their values.

!IMPORTANT: Sets, TypedArrays, and symbol properties of objects are not supported right now and might result in unexpected behavior. Please open an issue in the Remeda github project if you need support for these types.

The result would be narrowed to the second value so that the function can be used as a type guard.

Data First

R.isDeepEqual(data, other);
Parameters
data
The first value to compare.
other
The second value to compare.
Returns
boolean
R.isDeepEqual(1, 1); //=> true
R.isDeepEqual(1, "1"); //=> false
R.isDeepEqual([1, 2, 3], [1, 2, 3]); //=> true

Data Last

R.isDeepEqual(other)(data);
Parameters
other
The second value to compare.
Returns
Object
R.pipe(1, R.isDeepEqual(1)); //=> true
R.pipe(1, R.isDeepEqual("1")); //=> false
R.pipe([1, 2, 3], R.isDeepEqual([1, 2, 3])); //=> true

isDefined

Strict

A function that checks if the passed parameter is defined and narrows its type accordingly. To test specifically for undefined (and not null) use the strict variant of this function.

! DEPRECATED: If your type accepts null use R.isNullish(data), otherwise prefer R.isDefined.strict(data). The non-strict version will be removed in V2!

R.isDefined(data);
Parameters
data
The variable to check.
Returns
boolean
R.isDefined("string"); //=> true
R.isDefined(null); //=> false
R.isDefined(undefined); //=> false
R.isDefined.strict(null); //=> true
R.isDefined.strict(undefined); //=> false

isEmpty

A function that checks if the passed parameter is empty.

undefined is also considered empty, but only when it's in a union with a string or string-like type.

This guard doesn't work negated because of typescript limitations! If you need to check that an array is not empty, use R.hasAtLeast(data, 1) and not !R.isEmpty(data). For strings and objects there's no way in typescript to narrow the result to a non-empty type.

R.isEmpty(data);
Parameters
data
The variable to check.
Returns
boolean
R.isEmpty(undefined); //=>true
R.isEmpty(""); //=> true
R.isEmpty([]); //=> true
R.isEmpty({}); //=> true
R.isEmpty("test"); //=> false
R.isEmpty([1, 2, 3]); //=> false
R.isEmpty({ length: 0 }); //=> false

isError

A function that checks if the passed parameter is an Error and narrows its type accordingly.

R.isError(data);
Parameters
data
The variable to check.
Returns
boolean
R.isError(new Error("message")); //=> true
R.isError("somethingElse"); //=> false

isFunction

A function that checks if the passed parameter is a Function and narrows its type accordingly.

R.isFunction(data);
Parameters
data
The variable to check.
Returns
boolean
R.isFunction(() => {}); //=> true
R.isFunction("somethingElse"); //=> false

isIncludedIn

Checks if the item is included in the container. This is a wrapper around Array.prototype.includes and Set.prototype.has and thus relies on the same equality checks that those functions do (which is reference equality, e.g. ===). In some cases the input's type is also narrowed to the container's item types.

Notice that unlike most functions, this function takes a generic item as it's data and an array as it's parameter.

Data First

R.isIncludedIn(data, container);
Parameters
data
The item that is checked.
container
The items that are checked against.
Returns
boolean
R.isIncludedIn(2, [1, 2, 3]); // => true
R.isIncludedIn(4, [1, 2, 3]); // => false

const data = "cat" as "cat" | "dog" | "mouse";
R.isIncludedIn(data, ["cat", "dog"] as const); // true (typed "cat" | "dog");

Data Last

R.isIncludedIn(container)(data);
Parameters
container
The items that are checked against.
Returns
Object
R.pipe(2, R.isIncludedIn([1, 2, 3])); // => true
R.pipe(4, R.isIncludedIn([1, 2, 3])); // => false

const data = "cat" as "cat" | "dog" | "mouse";
R.pipe(data, R.isIncludedIn(["cat", "dog"] as const)); // => true (typed "cat" | "dog");

isNonNull

A function that checks if the passed parameter is not null and narrows its type accordingly. Notice that undefined is not null!

R.isNonNull(data);
Parameters
data
The variable to check.
Returns
boolean
R.isNonNull("string"); //=> true
R.isNonNull(null); //=> false
R.isNonNull(undefined); //=> true

isNonNullish

Strict

A function that checks if the passed parameter is defined AND isn't null and narrows its type accordingly.

R.isNonNullish(data);
Parameters
data
The variable to check.
Returns
boolean
R.isNonNullish("string"); //=> true
R.isNonNullish(null); //=> false
R.isNonNullish(undefined); //=> false

isNot

A function that takes a guard function as predicate and returns a guard that negates it.

Data Last

R.isNot(R.isTruthy)(data);
Parameters
predicate
The guard function to negate.
Returns
Object
R.isNot(R.isTruthy)(false); //=> true
R.isNot(R.isTruthy)(true); //=> false

isNullish

A function that checks if the passed parameter is either null or undefined and narrows its type accordingly.

R.isNullish(data);
Parameters
data
The variable to check.
Returns
boolean
R.isNullish(undefined); //=> true
R.isNullish(null); //=> true
R.isNullish("somethingElse"); //=> false

isNumber

A function that checks if the passed parameter is a number and narrows its type accordingly.

R.isNumber(data);
Parameters
data
The variable to check.
Returns
boolean
R.isNumber(1); //=> true
R.isNumber("notANumber"); //=> false

isObjectType

Checks if the given parameter is of type "object" via typeof, excluding null.

It's important to note that in JavaScript, many entities are considered objects, like Arrays, Classes, RegExps, Maps, Sets, Dates, URLs, Promise, Errors, and more. Although technically an object too, null is not considered an object by this function, so that its easier to narrow nullables.

For a more specific check that is limited to plain objects (simple struct/shape/record-like objects), consider using isPlainObject instead. For a simpler check that only removes null from the type prefer isNonNull or isDefined.

Data First

R.isObjectType(data);
Parameters
data
The variable to be checked for being an object type.
Returns
boolean
// true
R.isObjectType({}); //=> true
R.isObjectType([]); //=> true
R.isObjectType(Promise.resolve("something")); //=> true
R.isObjectType(new Date()); //=> true
R.isObjectType(new Error("error")); //=> true

// false
R.isObjectType("somethingElse"); //=> false
R.isObjectType(null); //=> false

isPlainObject

Checks if data is a "plain" object. A plain object is defined as an object with string keys and values of any type, including primitives, other objects, functions, classes, etc (aka struct/shape/record/simple). Technically, a plain object is one whose prototype is either Object.prototype or null, ensuring it does not inherit properties or methods from other object types.

This function is narrower in scope than isObjectType, which accepts any entity considered an "object" by JavaScript's typeof.

Note that Maps, Arrays, and Sets are not considered plain objects and would return false.

R.isPlainObject(data);
Parameters
data
The variable to check.
Returns
boolean
// true
R.isPlainObject({}); //=> true
R.isPlainObject({ a: 123 }); //=> true

// false
R.isPlainObject([]); //=> false
R.isPlainObject(Promise.resolve("something")); //=> false
R.isPlainObject(new Date()); //=> false
R.isPlainObject(new Error("error")); //=> false
R.isPlainObject("somethingElse"); //=> false
R.isPlainObject(null); //=> false

isPromise

A function that checks if the passed parameter is a Promise and narrows its type accordingly.

R.isPromise(data);
Parameters
data
The variable to check.
Returns
boolean
R.isPromise(Promise.resolve(5)); //=> true
R.isPromise(Promise.reject(5)); //=> true
R.isPromise("somethingElse"); //=> false

isString

A function that checks if the passed parameter is a string and narrows its type accordingly.

R.isString(data);
Parameters
data
The variable to check.
Returns
boolean
R.isString("string"); //=> true
R.isString(1); //=> false

isSymbol

A function that checks if the passed parameter is a symbol and narrows its type accordingly.

R.isSymbol(data);
Parameters
data
The variable to check.
Returns
boolean
R.isSymbol(Symbol("foo")); //=> true
R.isSymbol(1); //=> false

isTruthy

A function that checks if the passed parameter is truthy and narrows its type accordingly.

R.isTruthy(data);
Parameters
data
The variable to check.
Returns
boolean
R.isTruthy("somethingElse"); //=> true
R.isTruthy(null); //=> false
R.isTruthy(undefined); //=> false
R.isTruthy(false); //=> false
R.isTruthy(0); //=> false
R.isTruthy(""); //=> false

Number

add

Adds two numbers.

Data First

R.add(value, addend);
Parameters
value
The number.
addend
The number to add to the value.
Returns
number
R.add(10, 5); // => 15
R.add(10, -5); // => 5
R.reduce([1, 2, 3, 4], R.add, 0); // => 10

Data Last

R.add(addend)(value);
Parameters
addend
The number to add to the value.
Returns
Object
R.add(5)(10); // => 15
R.add(-5)(10); // => 5
R.map([1, 2, 3, 4], R.add(1)); // => [2, 3, 4, 5]

ceil

Rounds up a given number to a specific precision. If you'd like to round up to an integer (i.e. use this function with constant precision === 0), use Math.ceil instead, as it won't incur the additional library overhead.

Data First

R.ceil(value, precision);
Parameters
value
The number to round up.
precision
The precision to round up to. Must be an integer between -15 and 15.
Returns
number
R.ceil(123.9876, 3); // => 123.988
R.ceil(483.22243, 1); // => 483.3
R.ceil(8541, -1); // => 8550
R.ceil(456789, -3); // => 457000

Data Last

R.ceil(precision)(value);
Parameters
precision
The precision to round up to. Must be an integer between -15 and 15.
Returns
Object
R.ceil(3)(123.9876); // => 123.988
R.ceil(1)(483.22243); // => 483.3
R.ceil(-1)(8541); // => 8550
R.ceil(-3)(456789); // => 457000

clamp

Clamp the given value within the inclusive min and max bounds.

Data First

R.clamp(value, { min, max });
Parameters
value
The number.
limits
The bounds limits.
Returns
number
clamp(10, { min: 20 }); // => 20
clamp(10, { max: 5 }); // => 5
clamp(10, { max: 20, min: 5 }); // => 10

Data Last

R.clamp({ min, max })(value);
Parameters
limits
The bounds limits.
Returns
Object
clamp({ min: 20 })(10); // => 20
clamp({ max: 5 })(10); // => 5
clamp({ max: 20, min: 5 })(10); // => 10

divide

Divides two numbers.

Data First

R.divide(value, divisor);
Parameters
value
The number.
divisor
The number to divide the value by.
Returns
number
R.divide(12, 3); // => 4
R.reduce([1, 2, 3, 4], R.divide, 24); // => 1

Data Last

R.divide(divisor)(value);
Parameters
divisor
The number to divide the value by.
Returns
Object
R.divide(3)(12); // => 4
R.map([2, 4, 6, 8], R.divide(2)); // => [1, 2, 3, 4]

floor

Rounds down a given number to a specific precision. If you'd like to round down to an integer (i.e. use this function with constant precision === 0), use Math.floor instead, as it won't incur the additional library overhead.

Data First

R.floor(value, precision);
Parameters
value
The number to round down.
precision
The precision to round down to. Must be an integer between -15 and 15.
Returns
number
R.floor(123.9876, 3); // => 123.987
R.floor(483.22243, 1); // => 483.2
R.floor(8541, -1); // => 8540
R.floor(456789, -3); // => 456000

Data Last

R.floor(precision)(value);
Parameters
precision
The precision to round down to. Must be an integer between -15 and 15.
Returns
Object
R.floor(3)(123.9876); // => 123.987
R.floor(1)(483.22243); // => 483.2
R.floor(-1)(8541); // => 8540
R.floor(-3)(456789); // => 456000

multiply

Multiplies two numbers.

Data First

R.multiply(value, multiplicand);
Parameters
value
The number.
multiplicand
The number to multiply the value by.
Returns
number
R.multiply(3, 4); // => 12
R.reduce([1, 2, 3, 4], R.multiply, 1); // => 24

Data Last

R.multiply(multiplicand)(value);
Parameters
multiplicand
The number to multiply the value by.
Returns
Object
R.multiply(4)(3); // => 12
R.map([1, 2, 3, 4], R.multiply(2)); // => [2, 4, 6, 8]

product

Compute the product of the numbers in the array, or return 1 for an empty array.

Data First

R.product(data);
Parameters
data
The array of numbers.
Returns
number
R.product([1, 2, 3]); // => 6
R.product([]); // => 1

Data Last

R.product()(data);
Parameters
Returns
Object
R.pipe([1, 2, 3], R.product()); // => 6
R.pipe([], R.product()); // => 0

round

Rounds a given number to a specific precision. If you'd like to round to an integer (i.e. use this function with constant precision === 0), use Math.round instead, as it won't incur the additional library overhead.

Data First

R.round(value, precision);
Parameters
value
The number to round.
precision
The precision to round to. Must be an integer between -15 and 15.
Returns
number
R.round(123.9876, 3); // => 123.988
R.round(483.22243, 1); // => 483.2
R.round(8541, -1); // => 8540
R.round(456789, -3); // => 457000

Data Last

R.round(precision)(value);
Parameters
precision
The precision to round to. Must be an integer between -15 and 15.
Returns
Object
R.round(3)(123.9876); // => 123.988
R.round(1)(483.22243); // => 483.2
R.round(-1)(8541); // => 8540
R.round(-3)(456789); // => 457000

subtract

Subtracts two numbers.

Data First

R.subtract(value, subtrahend);
Parameters
value
The number.
subtrahend
The number to subtract from the value.
Returns
number
R.subtract(10, 5); // => 5
R.subtract(10, -5); // => 15
R.reduce([1, 2, 3, 4], R.subtract, 20); // => 10

Data Last

R.subtract(subtrahend)(value);
Parameters
subtrahend
The number to subtract from the value.
Returns
Object
R.subtract(5)(10); // => 5
R.subtract(-5)(10); // => 15
R.map([1, 2, 3, 4], R.subtract(1)); // => [0, 1, 2, 3]

sum

Sums the numbers in the array, or return 0 for an empty array.

Data First

R.sum(data);
Parameters
data
The array of numbers.
Returns
number
R.sum([1, 2, 3]); // => 6
R.sum([]); // => 0

Data Last

R.sum()(data);
Parameters
Returns
Object
R.pipe([1, 2, 3], R.sum()); // => 6
R.pipe([], R.sum()); // => 0

Object

addProp

Add a new property to an object.

Data First

R.addProp(obj, prop, value);
Parameters
obj
The target object.
prop
The property name.
value
The property value.
Returns
Object
R.addProp({ firstName: "john" }, "lastName", "doe"); // => {firstName: 'john', lastName: 'doe'}

Data Last

R.addProp(prop, value)(obj);
Parameters
prop
The property name.
value
The property value.
Returns
Object
R.addProp("lastName", "doe")({ firstName: "john" }); // => {firstName: 'john', lastName: 'doe'}

clone

Creates a deep copy of the value. Supported types: Array, Object, Number, String, Boolean, Date, RegExp. Functions are assigned by reference rather than copied.

Breaking changes in v2

Runtime

The function no longer tries to use a clone function of the cloned object. It will only clone based on our own cloning logic. If you want to use an object’s exported clone function you will need to call it directly.

We no longer support “headless” invocations. In dataLast invocations call the function with an empty args list (e.g. clone()).

Examples
clone function
type MyType = {
  readonly clone: () => MyType;
};

declare const DATA: MyType;

// Was
const cloned = clone(DATA);

// Now
const cloned = DATA.clone();
Headless
// Was
const cloned = map(DATA, clone);

// Now
const cloned = map(DATA, clone());
R.clone(value);
Parameters
value
The object to clone.
Returns
Object

entries

Strict

Returns an array of key/values of the enumerable properties of an object.

Breaking changes in v2

Typing

If you weren’t using the strict variant, The returned type is now stricter and more reflective of the input type. The returned entries are typed according to to the keys in the input, with the values typed per-key. If you are already using entries.strict simply remove the .strict suffix.

Also, the return type now filters symbol keys, and casts number keys as strings.

Examples
Strict
// Was
entries.strict(obj);

// Now
entries(obj);
Better typing
const result = entries({ a: 123 } as const);
//    ^? ['a', 123][], Was: [string, number][]
Symbol keys
const mySymbol = Symbol("a");

// Was
const result = entries.strict({ [mySymbol]: 123, a: 456 } as const);
//    ^? ([typeof mySymbol, 123] | [a, 456])[]

// Now
const result = entries({ [mySymbol]: 123, a: 456 } as const);
//    ^? ['a', 456][]
Number keys
// Was
const result = entries.strict({ 123: "hello" } as const);
//    ^? [123, "hello"][]

// Now
const result = entries({ 123: "hello" } as const);
//    ^? ['123', "hello"][]

Data First

R.entries(object);
Parameters
object
Object to return keys and values of.
Returns
Array
R.entries({ a: 1, b: 2, c: 3 }); // => [['a', 1], ['b', 2], ['c', 3]]
R.entries.strict({ a: 1 } as const); // => [['a', 1]] typed Array<['a', 1]>

Data Last

R.entries()(object);
Parameters
Returns
Object
R.pipe({ a: 1, b: 2, c: 3 }, entries()); // => [['a', 1], ['b', 2], ['c', 3]]
R.pipe({ a: 1 } as const, entries.strict()); // => [['a', 1]] typed Array<['a', 1]>

evolve

Creates a new object by applying functions that is included in evolver object parameter to the data object parameter according to their corresponding path.

Functions included in evolver object will not be invoked if its corresponding key does not exist in the data object. Also, values included in data object will be kept as is if its corresponding key does not exist in the evolver object.

Breaking changes in v2

Typing

Symbol keys were previously skipped silently during runtime, the typing is now fixed to prevent defining transformers for these keys.

Examples
Symbol keys
const mySymbol = Symbol("a");

// This was fine in v1, will now raise a Typescript error.
evolve({ [mySymbol]: 123 }, { [mySymbol]: add(1) });

Data First

R.evolve(data, evolver);
Parameters
object
Object whose value is applied to the corresponding function that is defined in `evolver` at the same path.
evolver
Object that include functions that is applied to the corresponding value of `data` object at the same path.
Returns
Object
const evolver = {
  count: add(1),
  time: { elapsed: add(1), remaining: add(-1) },
};
const data = {
  id: 10,
  count: 10,
  time: { elapsed: 100, remaining: 1400 },
};
evolve(data, evolver);
// => {
//   id: 10,
//   count: 11,
//   time: { elapsed: 101, remaining: 1399 },
// }

Data Last

R.evolve(evolver)(data);
Parameters
evolver
Object that include functions that is applied to the corresponding value of `data` object at the same path.
Returns
Object
const evolver = {
  count: add(1),
  time: { elapsed: add(1), remaining: add(-1) },
};
const data = {
  id: 10,
  count: 10,
  time: { elapsed: 100, remaining: 1400 },
};
R.pipe(object, R.evolve(evolver));
// => {
//   id: 10,
//   count: 11,
//   time: { elapsed: 101, remaining: 1399 },
// }

forEachObj

Iterate an object using a defined callback function. The original object is returned.

Data First

R.forEachObj(object, fn);
Parameters
object
The object.
fn
The callback function.
Returns
Object
R.forEachObj({ a: 1 }, (val) => {
  console.log(`${val}`);
}); // "1"
R.forEachObj.indexed({ a: 1 }, (val, key, obj) => {
  console.log(`${key}: ${val}`);
}); // "a: 1"

Data Last

R.forEachObj(fn)(object);
Parameters
fn
The callback function.
Returns
Object
R.pipe(
  { a: 1 },
  R.forEachObj((val) => console.log(`${val}`)),
); // "1"
R.pipe(
  { a: 1 },
  R.forEachObj.indexed((val, key) => console.log(`${key}: ${val}`)),
); // "a: 1"

fromEntries

Strict

Creates a new object from an array of tuples by pairing up first and second elements as {[key]: value}. If a tuple is not supplied for any element in the array, the element will be ignored If duplicate keys exist, the tuple with the greatest index in the input array will be preferred.

The strict option supports more sophisticated use-cases like those that would result when calling the strict toPairs function.

There are several other functions that could be used to build an object from an array:

  • fromKeys - Builds an object from an array of keys and a mapper for values.
  • indexBy - Builds an object from an array of values and a mapper for keys.
  • pullObject - Builds an object from an array of items with mappers for both keys and values.
  • mapToObj - Builds an object from an array of items and a single mapper for key-value pairs. Refer to the docs for more details.

Data First

R.fromEntries(tuples);
Parameters
entries
The list of input tuples.
Returns
Object
R.fromEntries([
  ["a", "b"],
  ["c", "d"],
]); // => {a: 'b', c: 'd'} (type: Record<string, string>)
R.fromEntries.strict(["a", 1] as const); // => {a: 1} (type: {a: 1})

Data Last

R.fromEntries()(tuples);
Parameters
Returns
Object
R.pipe(
  [
    ["a", "b"],
    ["c", "d"],
  ],
  R.fromEntries(),
); // => {a: 'b', c: 'd'} (type: Record<string, string>)
R.pipe(["a", 1] as const, R.fromEntries.strict()); // => {a: 1} (type: {a: 1})

fromKeys

Creates an object that maps each key in data to the result of mapper for that key. Duplicate keys are overwritten, guaranteeing that mapper is run for each item in data.

There are several other functions that could be used to build an object from an array:

  • indexBy - Builds an object from an array of values and a mapper for keys.
  • pullObject - Builds an object from an array of items with mappers for both keys and values.
  • fromEntries - Builds an object from an array of key-value pairs.
  • mapToObj - Builds an object from an array of items and a single mapper for key-value pairs. Refer to the docs for more details.

Data First

R.fromKeys(data, mapper);
Parameters
data
An array of keys of the output object. All items in the array would be keys in the output array.
mapper
Takes a key and returns the value that would be associated with that key.
Returns
Object
R.fromKeys(["cat", "dog"], R.length()); // { cat: 3, dog: 3 } (typed as Partial<Record<"cat" | "dog", number>>)
R.fromKeys([1, 2], R.add(1)); // { 1: 2, 2: 3 } (typed as Partial<Record<1 | 2, number>>)

Data Last

R.fromKeys(mapper)(data);
Parameters
mapper
Takes a key and returns the value that would be associated with that key.
Returns
Object
R.pipe(["cat", "dog"], R.fromKeys(R.length())); // { cat: 3, dog: 3 } (typed as Partial<Record<"cat" | "dog", number>>)
R.pipe([1, 2], R.fromKeys(R.add(1))); // { 1: 2, 2: 3 } (typed as Partial<Record<1 | 2, number>>)

invert

Lazy

Returns an object whose keys and values are swapped. If the object contains duplicate values, subsequent values will overwrite previous values.

Data First

R.invert(object);
Parameters
object
The object.
Returns
Object
R.invert({ a: "d", b: "e", c: "f" }); // => { d: "a", e: "b", f: "c" }

Data Last

R.invert()(object);
Parameters
Returns
Object
R.pipe({ a: "d", b: "e", c: "f" }, R.invert()); // => { d: "a", e: "b", f: "c" }

keys

StrictLazy

Returns a new array containing the keys of the array or object.

Breaking changes in v2

Typing

If you weren’t using the strict variant, the returned type is now stricter and more reflective of the input type. Instead of string, the keys are typed based on the input type (similar to how keyof T works), all number keys are cast as strings, and symbol keys are filtered out. For tuple inputs the returned array is of the same shape (e.g. if the input is non-empty, the output would also be non-empty). This aligns with the runtime behavior of Object.keys. To achieve the legacy typing cast your object as a Record<string, unknown>. If you are already using the strict variant simply remove the .strict suffix.

The “headless” dataLast form is no longer supported, use the functional style instead.

Examples
Strict
// Was
keys.strict(obj);

// Now
keys(obj);
Better typing
const result = keys({ a: 123 } as const);
//    ^? "a"[], Was: string[]
Symbol keys
const mySymbol = Symbol("a");
keys({ [mySymbol]: 123 }); // => [];
Number keys
keys({ 123: "hello" }); // ["123"];
Legacy
const result = keys({ 123: "hello" } as Record<string, unknown>);
//    ^? string[]
No headless version
// Was
pipe(DATA, keys);

// Now
pipe(DATA, keys());

Data First

R.keys(source);
Parameters
source
Either an array or an object.
Returns
Array
R.keys(["x", "y", "z"]); // => ['0', '1', '2']
R.keys({ a: "x", b: "y", c: "z" }); // => ['a', 'b', 'c']
R.keys.strict({ a: "x", b: "y", 5: "z" } as const); // => ['a', 'b', '5'], typed Array<'a' | 'b' | '5'>
R.pipe(["x", "y", "z"], R.keys); // => ['0', '1', '2']
R.pipe({ a: "x", b: "y", c: "z" }, R.keys); // => ['a', 'b', 'c']
R.pipe({ a: "x", b: "y", c: "z" }, R.keys, R.first()); // => 'a'
R.pipe({ a: "x", b: "y", 5: "z" } as const, R.keys.strict); // => ['a', 'b', '5'], typed Array<'a' | 'b' | '5'>

mapKeys

Maps keys of object and keeps the same values.

Data First

R.mapKeys(object, fn);
Parameters
data
The object to map.
fn
The mapping function.
Returns
Object
R.mapKeys({ a: 1, b: 2 }, (key, value) => key + value); // => { a1: 1, b2: 2 }

Data Last

R.mapKeys(fn)(object);
Parameters
fn
The mapping function.
Returns
Object
R.pipe(
  { a: 1, b: 2 },
  R.mapKeys((key, value) => key + value),
); // => { a1: 1, b2: 2 }

mapValues

Maps values of object and keeps the same keys.

Data First

R.mapValues(object, fn);
Parameters
data
The object to map.
fn
The mapping function.
Returns
Object
R.mapValues({ a: 1, b: 2 }, (value, key) => value + key); // => {a: '1a', b: '2b'}

Data Last

R.mapValues(fn)(object);
Parameters
fn
The mapping function.
Returns
Object
R.pipe(
  { a: 1, b: 2 },
  R.mapValues((value, key) => value + key),
); // => {a: '1a', b: '2b'}

merge

Merges two objects into one by combining their properties, effectively creating a new object that incorporates elements from both. The merge operation prioritizes the second object's properties, allowing them to overwrite those from the first object with the same names.

Equivalent to { ...data, ...source }.

Data First

R.merge(data, source);
Parameters
data
The destination object, serving as the basis for the merge. Properties from this object are included in the new object, but will be overwritten by properties from the source object with matching keys.
source
The source object, whose properties will be included in the new object. If properties in this object share keys with properties in the destination object, the values from the source object will be used in the new object.
Returns
Object
R.merge({ x: 1, y: 2 }, { y: 10, z: 2 }); // => { x: 1, y: 10, z: 2 }

Data Last

R.merge(source)(data);
Parameters
source
The source object, whose properties will be included in the new object. If properties in this object share keys with properties in the destination object, the values from the source object will be used in the new object.
Returns
Object
R.pipe({ x: 1, y: 2 }, R.merge({ y: 10, z: 2 })); // => { x: 1, y: 10, z: 2 }

mergeDeep

Merges the source object into the destination object. The merge is similar to performing { ...destination, ... source } (where disjoint values from each object would be copied as-is, and for any overlapping props the value from source would be used); But for each prop (p), if both destination and source have a plain-object as a value, the value would be taken as the result of recursively deepMerging them (result.p === deepMerge(destination.p, source.p)).

Data First

R.mergeDeep(destination, source);
Parameters
destination
The object to merge into. In general, this object would have it's values overridden.
source
The object to merge from. In general, shared keys would be taken from this object.
Returns
Object
R.mergeDeep({ foo: "bar", x: 1 }, { foo: "baz", y: 2 }); // => { foo: 'baz', x: 1, y: 2 }

Data Last

R.mergeDeep(source)(destination);
Parameters
source
The object to merge from. In general, shared keys would be taken from this object.
Returns
Object
R.pipe({ foo: "bar", x: 1 }, R.mergeDeep({ foo: "baz", y: 2 })); // => { foo: 'baz', x: 1, y: 2 }

objOf

Creates an object containing a single key:value pair.

R.objOf(value, key);
Parameters
value
The object value.
key
The property name.
Returns
Object
R.objOf(10, "a"); // => { a: 10 }
R.objOf(key)(value);
Parameters
key
The property name.
Returns
Object
R.pipe(10, R.objOf("a")); // => { a: 10 }

omit

Returns a partial copy of an object omitting the keys specified.

Breaking changes in v2

Runtime

Props with symbol keys are no longer omitted implicitly from the output, they can be targeted from omission.

Examples
Symbol keys aren’t omitted
const mySymbol = Symbol("a");
const DATA = { [mySymbol]: "hello", a: 123 };
const result = omit(DATA, ["a"]); // => { [mySymbol]: "hello" }, Was: {}
Symbol keys can be omitted
const symbolA = Symbol("a");
const symbolB = Symbol("b");
const DATA = { [symbolA]: "hello", [symbolB]: 123 };
const result = omit(DATA, [symbolA]); // => { [symbolB]: 123 }, Was: {}

Data Last

R.omit(names)(obj);
Parameters
propNames
The property names.
Returns
Object
R.pipe({ a: 1, b: 2, c: 3, d: 4 }, R.omit(["a", "d"])); // => { b: 2, c: 3 }

Data First

R.omit(obj, names);
Parameters
data
The object.
propNames
The property names.
Returns
Object
R.omit({ a: 1, b: 2, c: 3, d: 4 }, ["a", "d"]); // => { b: 2, c: 3 }

omitBy

Returns a partial copy of an object omitting the keys matching predicate.

Data First

R.omitBy(object, fn);
Parameters
object
The target object.
fn
The predicate.
Returns
Object
R.omitBy({ a: 1, b: 2, A: 3, B: 4 }, (val, key) => key.toUpperCase() === key); // => {a: 1, b: 2}

Data Last

R.omitBy(fn)(object);
Parameters
fn
The predicate.
Returns
Object
R.omitBy((val, key) => key.toUpperCase() === key)({ a: 1, b: 2, A: 3, B: 4 }); // => {a: 1, b: 2}

pathOr

Gets the value at path of object. If the resolved value is null or undefined, the defaultValue is returned in its place.

Data First

R.pathOr(object, array, defaultValue);
Parameters
object
The target object.
path
The path of the property to get.
defaultValue
The default value.
Returns
Object
R.pathOr({ x: 10 }, ["y"], 2); // 2
R.pathOr({ y: 10 }, ["y"], 2); // 10

Data Last

R.pathOr(array, defaultValue)(object);
Parameters
path
The path of the property to get.
defaultValue
The default value.
Returns
Object
R.pipe({ x: 10 }, R.pathOr(["y"], 2)); // 2
R.pipe({ y: 10 }, R.pathOr(["y"], 2)); // 10

pick

Creates an object composed of the picked object properties.

Data Last

R.pick([prop1, prop2])(object);
Parameters
names
The properties names.
Returns
Object
R.pipe({ a: 1, b: 2, c: 3, d: 4 }, R.pick(["a", "d"])); // => { a: 1, d: 4 }

Data First

R.pick(object, [prop1, prop2]);
Parameters
object
The target object.
names
The properties names.
Returns
Object
R.pick({ a: 1, b: 2, c: 3, d: 4 }, ["a", "d"]); // => { a: 1, d: 4 }

pickBy

Creates an object composed of the picked object properties.

Data First

R.pickBy(object, fn);
Parameters
object
The target object.
fn
The predicate.
Returns
Object
R.pickBy({ a: 1, b: 2, A: 3, B: 4 }, (val, key) => key.toUpperCase() === key); // => {A: 3, B: 4}

Data Last

R.pickBy(fn)(object);
Parameters
fn
The predicate.
Returns
Object
R.pickBy((val, key) => key.toUpperCase() === key)({ a: 1, b: 2, A: 3, B: 4 }); // => {A: 3, B: 4}

prop

Gets the value of the given property.

Data Last

R.prop(prop)(object);
Parameters
propName
The property name.
Returns
Object
R.pipe({ foo: "bar" }, R.prop("foo")); // => 'bar'

pullObject

Creates an object that maps the result of valueExtractor with a key resulting from running keyExtractor on each item in data. Duplicate keys are overwritten, guaranteeing that the extractor functions are run on each item in data.

There are several other functions that could be used to build an object from an array:

  • fromKeys - Builds an object from an array of keys and a mapper for values.
  • indexBy - Builds an object from an array of values and a mapper for keys.
  • fromEntries - Builds an object from an array of key-value pairs.
  • mapToObj - Builds an object from an array of items and a single mapper for key-value pairs. Refer to the docs for more details.

Data First

R.pullObject(data, keyExtractor, valueExtractor);
Parameters
data
The items used to pull/extract the keys and values from.
keyExtractor
Computes the key for item.
valueExtractor
Computes the value for the item.
Returns
Object
R.pullObject(
  [
    { name: "john", email: "john@remedajs.com" },
    { name: "jane", email: "jane@remedajs.com" },
  ],
  R.prop("name"),
  R.prop("email"),
); // => { john: "john@remedajs.com", jane: "jane@remedajs.com" }

Data Last

R.pullObject(keyExtractor, valueExtractor)(data);
Parameters
keyExtractor
Computes the key for item.
valueExtractor
Computes the value for the item.
Returns
Object
R.pipe(
  [
    { name: "john", email: "john@remedajs.com" },
    { name: "jane", email: "jane@remedajs.com" },
  ],
  R.pullObject(R.prop("email"), R.prop("name")),
); // => { john: "john@remedajs.com", jane: "jane@remedajs.com" }

set

Sets the value at prop of object.

Data First

R.set(obj, prop, value);
Parameters
obj
The target method.
prop
The property name.
value
The value to set.
Returns
Object
R.set({ a: 1 }, "a", 2); // => { a: 2 }

Data Last

R.set(prop, value)(obj);
Parameters
prop
The property name.
value
The value to set.
Returns
Object
R.pipe({ a: 1 }, R.set("a", 2)); // => { a: 2 }

setPath

Sets the value at path of object.

Data First

R.setPath(obj, path, value);
Parameters
object
The target method.
path
The array of properties.
value
The value to set.
Returns
Object
R.setPath({ a: { b: 1 } }, ["a", "b"], 2); // => { a: { b: 2 } }

Data Last

R.setPath(path, value)(obj);
Parameters
path
The array of properties.
value
The value to set.
Returns
Object
R.pipe({ a: { b: 1 } }, R.setPath(["a", "b"], 2)); // { a: { b: 2 } }

swapProps

Swaps the positions of two properties in an object based on the provided keys.

Data First

swap(data, key1, key2);
Parameters
data
The object to be manipulated.
key1
The first property key.
key2
The second property key.
Returns
Object
swap({ a: 1, b: 2, c: 3 }, "a", "b"); // => {a: 2, b: 1, c: 3}

Data Last

swap(key1, key2)(data);
Parameters
key1
The first property key.
key2
The second property key.
Returns
Object
swap("a", "b")({ a: 1, b: 2, c: 3 }); // => {a: 2, b: 1, c: 3}

uniqueWith

Returns a new array containing only one copy of each element in the original list. Elements are compared by custom comparator isEquals.

Data First

R.uniqueWith(array, isEquals);
Parameters
array
The array to filter.
isEquals
The comparator.
Returns
Array
R.uniqueWith(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }],
  R.equals,
); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]

Data Last

R.uniqueWith(isEquals)(array);
Parameters
isEquals
The comparator.
Returns
Object
R.uniqueWith(R.equals)([
  { a: 1 },
  { a: 2 },
  { a: 2 },
  { a: 5 },
  { a: 1 },
  { a: 6 },
  { a: 7 },
]); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]
R.pipe(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }], // only 4 iterations
  R.uniqueWith(R.equals),
  R.take(3),
); // => [{a: 1}, {a: 2}, {a: 5}]

values

Lazy

Returns a new array containing the values of the array or object.

Breaking changes in v2

Typing

Values for symbol keys were never returned by the function in runtime, but the typing didn’t filter them out. This has been fixed.

The “headless” dataLast form is no longer supported, use the functional style instead.

Examples
Symbol keys
const mySymbol = Symbol("a");
const result = values({ [mySymbol]: 123, a: "hello" });
//    ^? string[], Was: (string | number)[]
No headless version
// Was
pipe(DATA, values);

// Now
pipe(DATA, values());

Data First

R.values(source);
Parameters
data
Either an array or an object.
Returns
Object
R.values(["x", "y", "z"]); // => ['x', 'y', 'z']
R.values({ a: "x", b: "y", c: "z" }); // => ['x', 'y', 'z']
R.pipe(["x", "y", "z"], R.values); // => ['x', 'y', 'z']
R.pipe({ a: "x", b: "y", c: "z" }, R.values); // => ['x', 'y', 'z']
R.pipe({ a: "x", b: "y", c: "z" }, R.values, R.first); // => 'x'

Other

tap

Calls the given function with the given value, then returns the given value. The return value of the provided function is ignored.

This allows "tapping into" a function sequence in a pipe, to perform side effects on intermediate results.

Data First

R.tap(value, fn);
Parameters
value
The value to pass into the function.
fn
The function to call.
Returns
Object
R.tap("foo", console.log); // => "foo"

Data Last

R.tap(fn)(value);
Parameters
fn
The function to call.
Returns
Object
R.pipe(
  [-5, -1, 2, 3],
  R.filter((n) => n > 0),
  R.tap(console.log), // prints [2, 3]
  R.map((n) => n * 2),
); // => [4, 6]

times

Calls an input function n times, returning an array containing the results of those function calls.

fn is passed one argument: The current value of n, which begins at 0 and is gradually incremented to n - 1.

Data First

Parameters
count
A value between `0` and `n - 1`. Increments after each function call.
fn
The function to invoke. Passed one argument, the current value of `n`.
Returns
Array

Data Last

Parameters
fn
The function to invoke. Passed one argument, the current value of `n`.
Returns
Object

String

randomString

Random a non-cryptographic random string from characters a-zA-Z0-9.

Data First

R.randomString(length);
Parameters
length
The length of the random string.
Returns
string
R.randomString(5); // => aB92J
R.pipe(5, R.randomString); // => aB92J

sliceString

Strict

A data-last version of String.prototype.slice so it could be used in pipes.

NOTE: You don't need this function if you are calling it directly, just use String.prototype.slice directly. This function doesn't provide any type improvements over the built-in types.

Data Last

R.sliceString(indexStart)(string);
Parameters
indexStart
The index of the first character to include in the returned substring.
indexEnd
(optional) The index of the first character to exclude from the returned substring.
Returns
Object
R.sliceString(1)(`abcdefghijkl`); // => `bcdefghijkl`
R.sliceString(4, 7)(`abcdefghijkl`); // => `efg`

stringToPath

Converts a path string to an array of string keys (including array index access keys). !IMPORTANT!: Attempting to pass a simple string type will result in the result being inferred as never. This is intentional to help with type-safety as this function is primarily intended to help with other "object path access" functions like pathOr or setPath.

Data First

R.stringToPathArray(path);
Parameters
path
A string path.
Returns
Object

Deprecated

compact

Filter out all falsy values. The values false, null, 0, "", undefined, and NaN are falsy.

! DEPRECATED: Use R.filter(items, R.isTruthy). Will be removed in V2!

Breaking changes in v2

Removed

Replaced with filter with isTruthy as the predicate.

Examples
dataFirst
// Was
compact([1, null, 3]);

// Now
filter([1, null, 3], isTruthy);
dataLast
// Was
pipe([1, null, 3], compact);

// Now
pipe([1, null, 3], filter(isTruthy));
R.compact(array);
Parameters
items
The array to compact.
Returns
Array
R.compact([0, 1, false, 2, "", 3]); // => [1, 2, 3]

countBy

Indexed

Counts how many values of the collection pass the specified predicate.

! DEPRECATED: Use R.filter(items, fn).length. Will be removed in v2!

Data First

R.countBy(array, fn);
Parameters
items
The input data.
fn
The predicate.
Returns
number
R.countBy([1, 2, 3, 4, 5], (x) => x % 2 === 0); // => 2

Data Last

R.countBy(fn)(array);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [1, 2, 3, 4, 5],
  R.countBy((x) => x % 2 === 0),
); // => 2

createPipe

Creates a data-last pipe function. First function must be always annotated. Other functions are automatically inferred.

! DEPRECATED: Use R.piped(op1, op2, op3). Will be removed in V2!

R.createPipe(op1, op2, op3)(data);
Parameters
op1
Returns
Object
R.createPipe(
  (x: number) => x * 2,
  (x) => x * 3,
)(1); // => 6

equals

Returns true if its arguments are equivalent, false otherwise. NOTE: Doesn't handle cyclical data structures.

! DEPRECATED: Use R.isDeepEqual(a, b). Will be removed in V2.

Breaking changes in v2

Renamed

Called isDeepEqual instead. isDeepEqual has a slightly different return type (in some cases) allowing it to narrow the result.

Examples
Simple
// Was
equals({ a: { b: 123 } }, { a: { b: 456 } });

// Now
isDeepEqual({ a: { b: 123 } }, { a: { b: 456 } });
Type Predicate
// Was
const result = filter([] as ("cat" | "dog")[], equals("cat"));
//    ^? ("cat" | "dog")[]

// Now
const result = filter([] as ("cat" | "dog")[], isDeepEqual("cat"));
//    ^? "dog"[]

Data First

R.equals(a, b);
Parameters
a
The first object to compare.
b
The second object to compare.
Returns
boolean
R.equals(1, 1); //=> true
R.equals(1, "1"); //=> false
R.equals([1, 2, 3], [1, 2, 3]); //=> true

Data Last

R.equals(b)(a);
Parameters
a
The first object to compare.
Returns
Object
R.equals(1)(1); //=> true
R.equals("1")(1); //=> false
R.equals([1, 2, 3])([1, 2, 3]); //=> true

flatMapToObj

Indexed

Map each element of an array into an object using a defined callback function and flatten the result.

! DEPRECATED: Use R.fromEntries.strict(R.flatMap(array, fn)). Will be removed in V2!

Data First

R.flatMapToObj(array, fn);
Parameters
array
The array to map.
fn
The mapping function, which should return an Array of key-value pairs, similar to Object.fromEntries.
Returns
Object
R.flatMapToObj([1, 2, 3], (x) => (x % 2 === 1 ? [[String(x), x]] : [])); // => {1: 1, 3: 3}
R.flatMapToObj.indexed(["a", "b"], (x, i) => [
  [x, i],
  [x + x, i + i],
]); // => {a: 0, aa: 0, b: 1, bb: 2}

Data Last

R.flatMapToObj(fn)(array);
Parameters
fn
The mapping function, which should return an Array of key-value pairs, similar to Object.fromEntries.
Returns
Object
R.pipe(
  [1, 2, 3],
  R.flatMapToObj((x) => (x % 2 === 1 ? [[String(x), x]] : [])),
); // => {1: 1, 3: 3}
R.pipe(
  ["a", "b"],
  R.flatMapToObj.indexed((x, i) => [
    [x, i],
    [x + x, i + i],
  ]),
); // => {a: 0, aa: 0, b: 1, bb: 2}

flatten

Lazy

Flattens array a single level deep.

! DEPRECATED Use R.flat(data). Will be removed in V2!

Breaking changes in v2

Removed

Replaced with flat, with the optional “depth” param either left out, or set to 1.

Examples
dataFirst
// Was
flatten([[1, 2], [3], [4, 5]]);

// Now
flat([[1, 2], [3], [4, 5]]);

// Or
flat([[1, 2], [3], [4, 5]], 1 /* depth */);
dataLast
// Was
pipe([[1, 2], [3], [4, 5]], flatten());

// Now
pipe([[1, 2], [3], [4, 5]], flat());

// Or
pipe([[1, 2], [3], [4, 5]], flat(1 /* depth */));

Data First

R.flatten(array);
Parameters
items
The target array.
Returns
Array
R.flatten([[1, 2], [3], [4, 5]]); // => [1, 2, 3, 4, 5]

Data Last

R.flatten()(array);
Parameters
Returns
Object
R.pipe([[1, 2], [3], [4, 5]], R.flatten()); // => [1, 2, 3, 4, 5]

flattenDeep

Lazy

Recursively flattens array.

! DEPRECATED Use R.flat(data, 4). The typing for flattenDeep was broken for arrays nested more than 4 levels deep; this might lead to typing issues when migrating to the new function. Will be removed in V2!

R.flattenDeep(array);
Parameters
items
The target array.
Returns
Array
R.flattenDeep([
  [1, 2],
  [[3], [4, 5]],
]); // => [1, 2, 3, 4, 5]

Data Last

R.flattenDeep()(array);
Parameters
Returns
Object
R.pipe(
  [
    [1, 2],
    [[3], [4, 5]],
  ],
  R.flattenDeep(),
); // => [1, 2, 3, 4, 5]

fromPairs

Strict

Creates a new object from an array of tuples by pairing up first and second elements as {[key]: value}. If a tuple is not supplied for any element in the array, the element will be ignored If duplicate keys exist, the tuple with the greatest index in the input array will be preferred.

The strict option supports more sophisticated use-cases like those that would result when calling the strict toPairs function.

There are several other functions that could be used to build an object from an array:

  • fromKeys - Builds an object from an array of keys and a mapper for values.
  • indexBy - Builds an object from an array of values and a mapper for keys.
  • pullObject - Builds an object from an array of items with mappers for both keys and values.
  • mapToObj - Builds an object from an array of items and a single mapper for key-value pairs. Refer to the docs for more details.

! DEPRECATED: Use R.fromEntries(pairs), for dataLast invocations use the functional form R.fromEntries(). Will be removed in V2!

Data First

R.fromPairs(tuples);
Parameters
pairs
The list of input tuples.
Returns
Object
R.fromPairs([
  ["a", "b"],
  ["c", "d"],
]); // => {a: 'b', c: 'd'} (type: Record<string, string>)
R.fromPairs.strict(["a", 1] as const); // => {a: 1} (type: {a: 1})
R.pipe(
  [
    ["a", "b"],
    ["c", "d"],
  ],
  R.fromPairs,
); // => {a: 'b', c: 'd'} (type: Record<string, string>)
R.pipe(["a", 1] as const, R.fromPairs.strict); // => {a: 1} (type: {a: 1})

isNil

A function that checks if the passed parameter is Nil (null or undefined) and narrows its type accordingly.

! DEPRECATED: Use R.isNullish(data). Will be removed in V2!

R.isNil(data);
Parameters
data
The variable to check.
Returns
boolean
R.isNil(undefined); //=> true
R.isNil(null); //=> true
R.isNil("somethingElse"); //=> false

isObject

A function that checks if the passed parameter is of type Object and narrows its type accordingly.

! DEPRECATED: Use: R.isObjectType(data) && R.isNonNull(data) && !R.isArray(data) or R.isPlainObject(data). Will be removed in V2!

R.isObject(data);
Parameters
data
The variable to check.
Returns
boolean
R.isObject({}); //=> true
R.isObject(Promise.resolve("something")); //=> true
R.isObject(new Date()); //=> true
R.isObject(new Error("error")); //=> true
R.isObject("somethingElse"); //=> false

maxBy

Indexed

Returns the max element using the provided predicate.

! DEPRECATED: Use R.firstBy([fn, "desc"]). Will be removed in V2!

Data Last

R.maxBy(fn)(array);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [{ a: 5 }, { a: 1 }, { a: 3 }],
  R.maxBy((x) => x.a),
); // { a: 5 }

Data First

R.maxBy(array, fn);
Parameters
items
The array.
fn
The predicate.
Returns
Object
R.maxBy([{ a: 5 }, { a: 1 }, { a: 3 }], (x) => x.a); // { a: 5 }

minBy

Indexed

Returns the min element using the provided predicate.

! DEPRECATED: Use R.firstBy(fn). Will be removed in V2!

Data Last

R.minBy(fn)(array);
Parameters
fn
The predicate.
Returns
Object
R.pipe(
  [{ a: 5 }, { a: 1 }, { a: 3 }],
  R.minBy((x) => x.a),
); // { a: 1 }

Data First

R.minBy(array, fn);
Parameters
items
The array.
fn
The predicate.
Returns
Object
R.minBy([{ a: 5 }, { a: 1 }, { a: 3 }], (x) => x.a); // { a: 1 }

noop

A function that returns always undefined.

! DEPRECATED: Use R.constant(undefined), or R.doNothing() if the function doesn't need to return a value. Will be removed in V2!

Breaking changes in v2

Removed

Replaced with doNothing when the function should return void, or constant with undefined as the operand when a return value is required.

Examples
Void
// Was
forEach(DATA, noop);

// Now
forEach(DATA, doNothing());
Constant
// Was
map(DATA, noop);

// Now
map(DATA, constant(undefined));
R.noop();
Parameters
Returns
undefined
onSomething(R.noop);

reject

IndexedLazy

Reject the elements of an array that meet the condition specified in a callback function.

! DEPRECATED: Use R.filter(items, R.isNot(fn)). Will be removed in V2!

Breaking changes in v2

Removed

Replaced with filter with isNot wrapping the predicate. The result would now be narrowed if possible.

Examples
dataFirst
// Was
reject([1, "hello", 3], isString);

// Now
filter([1, "hello", 3], isNot(isString));
dataLast
// Was
pipe([1, "hello", 3], reject(isString));

// Now
pipe([1, "hello", 3], filter(isNot(isString)));
Narrowed result
// Was
const result = reject([1, "a"], isString);
//    ^? (number | string)[]

// Now
const result = filter([1, "a"], isNot(isString));
//    ^? number[]

Data First

R.reject(array, fn);
Parameters
items
The array to reject.
fn
The callback function.
Returns
Array
R.reject([1, 2, 3], (x) => x % 2 === 0); // => [1, 3]
R.reject.indexed([1, 2, 3], (x, i, array) => x % 2 === 0); // => [1, 3]

Data First

R.reject(array, fn);
Parameters
fn
The callback function.
Returns
Object
R.reject([1, 2, 3], (x) => x % 2 === 0); // => [1, 3]
R.reject.indexed([1, 2, 3], (x, i, array) => x % 2 === 0); // => [1, 3]

toPairs

Strict

Returns an array of key/values of the enumerable properties of an object.

! DEPRECATED Use R.entries(object), for dataLast invocations use the functional form R.entries(). Will be removed in V2!

Data First

R.toPairs(object);
Parameters
object
Object to return keys and values of.
Returns
Array
R.toPairs({ a: 1, b: 2, c: 3 }); // => [['a', 1], ['b', 2], ['c', 3]]
R.toPairs.strict({ a: 1 } as const); // => [['a', 1]] typed Array<['a', 1]>
R.pipe({ a: 1, b: 2, c: 3 }, toPairs); // => [['a', 1], ['b', 2], ['c', 3]]
R.pipe({ a: 1 } as const, toPairs.strict); // => [['a', 1]] typed Array<['a', 1]>

type

Gives a single-word string description of the (native) type of a value, returning such answers as 'Object', 'Number', 'Array', or 'Null'. Does not attempt to distinguish user Object types any further, reporting them all as 'Object'.

! DEPRECATED: Use typeof val, or one of the guards offered by this library. Will be removed in V2! We don't know what the use case for this function is. If you have a use case reach out via a GitHub issue so we can discuss this.

Breaking changes in v2

Removed

Use the built-in typeof and instanceof operators instead, or use a type- guard, isString, isNumber, isPlainObject, isArray, etc…

Examples
Object
const DATA = {};

// Was
type(DATA) === "Object";

// Now
typeof DATA === "object";

// Or
isPlainObject(DATA);
Number
const DATA = 123;

// Was
type(DATA) === "Number";

// Now
typeof DATA === "number";

// Or
isNumber(DATA);
Boolean
const DATA = true;

// Was
type(DATA) === "Boolean";

// Now
typeof DATA === "boolean";

// Or
isBoolean(DATA);
String
const DATA = "abc";

// Was
type(DATA) === "String";

// Now
typeof DATA === "string";

// Or
isString(DATA);
Null
const DATA = null;

// Was
type(DATA) === "Null";

// Now
DATA === null;
Array
const DATA = [];

// Was
type(DATA) === "Array";

// Now
Array.isArray(DATA);

// Or
isArray(DATA);
RegExp
const DATA = /abc/;

// Was
type(DATA) === "RegExp";

// Now
DATA instanceof RegExp;
Function
const DATA = () => {};

// Was
type(DATA) === "Function";

// Now
typeof DATA === "function";

// Or
isFunction(DATA);
Undefined
const DATA = undefined;

// Was
type(DATA) === "Undefined";

// Now
DATA === undefined;
R.type(obj);
Parameters
val
Value to return type of.
Returns
string
R.type({}); //=> "Object"
R.type(1); //=> "Number"
R.type(false); //=> "Boolean"
R.type("s"); //=> "String"
R.type(null); //=> "Null"
R.type([]); //=> "Array"
R.type(/[A-z]/); //=> "RegExp"
R.type(() => {}); //=> "Function"
R.type(undefined); //=> "Undefined"

uniq

Lazy

Returns a new array containing only one copy of each element in the original list. Elements are compared by reference using Set.

! DEPRECATED: Use R.unique(array). Will be removed in V2.

Breaking changes in v2

Renamed

Called unique instead.

Example
// Was
uniq(data);

// Now
unique(data);

Data First

R.uniq(array);
Parameters
array
The array to filter.
Returns
Array
R.uniq([1, 2, 2, 5, 1, 6, 7]); // => [1, 2, 5, 6, 7]

Data Last

R.uniq()(array);
Parameters
Returns
Object
R.pipe(
  [1, 2, 2, 5, 1, 6, 7], // only 4 iterations
  R.uniq(),
  R.take(3),
); // => [1, 2, 5]

uniqBy

Lazy

Returns a new array containing only one copy of each element in the original list transformed by a function. Elements are compared by reference using Set.

! DEPRECATED: Use R.uniqueBy(array, fn). Will be removed in V2!

Data First

R.uniqBy(array, fn);
Parameters
array
The array to filter.
transformer
Returns
Array
R.uniqBy(
  [{ n: 1 }, { n: 2 }, { n: 2 }, { n: 5 }, { n: 1 }, { n: 6 }, { n: 7 }],
  (obj) => obj.n,
); // => [{n: 1}, {n: 2}, {n: 5}, {n: 6}, {n: 7}]

Data Last

R.uniqBy(fn)(array);
Parameters
transformer
Returns
Object
R.pipe(
  [{ n: 1 }, { n: 2 }, { n: 2 }, { n: 5 }, { n: 1 }, { n: 6 }, { n: 7 }], // only 4 iterations
  R.uniqBy((obj) => obj.n),
  R.take(3),
); // => [{n: 1}, {n: 2}, {n: 5}]

uniqWith

Returns a new array containing only one copy of each element in the original list. Elements are compared by custom comparator isEquals.

! DEPRECATED: Use R.uniqueWith(array, isEquals). Will be removed in V2!

Data First

R.uniqWith(array, isEquals);
Parameters
array
The array to filter.
isEquals
The comparator.
Returns
Array
R.uniqWith(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }],
  R.equals,
); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]

Data Last

R.uniqWith(isEquals)(array);
Parameters
isEquals
The comparator.
Returns
Object
R.uniqWith(R.equals)([
  { a: 1 },
  { a: 2 },
  { a: 2 },
  { a: 5 },
  { a: 1 },
  { a: 6 },
  { a: 7 },
]); // => [{a: 1}, {a: 2}, {a: 5}, {a: 6}, {a: 7}]
R.pipe(
  [{ a: 1 }, { a: 2 }, { a: 2 }, { a: 5 }, { a: 1 }, { a: 6 }, { a: 7 }], // only 4 iterations
  R.uniqWith(R.equals),
  R.take(3),
); // => [{a: 1}, {a: 2}, {a: 5}]

zipObj

Creates a new object from two supplied lists by pairing up equally-positioned items. Key/value pairing is truncated to the length of the shorter of the two lists.

! DEPRECATED: Use R.fromEntries.strict(R.zip(first, second)). Will be removed in V2!

Data First

R.zipObj(first, second);
Parameters
first
The first input list.
second
The second input list.
Returns
Object
R.zipObj(["a", "b"], [1, 2]); // => {a: 1, b: 2}

Data Last

R.zipObj(second)(first);
Parameters
second
The second input list.
Returns
Object
R.zipObj([1, 2])(["a", "b"]); // => {a: 1, b: 2}