An open API service indexing awesome lists of open source software.

https://github.com/selfrefactor/rambda-v9


https://github.com/selfrefactor/rambda-v9

Last synced: 3 months ago
JSON representation

Awesome Lists containing this project

README

          

# Rambda

`Rambda` is smaller and faster alternative to the popular functional programming library **Ramda**. - [Documentation](https://selfrefactor.github.io/rambda/#/)

[![codecov](https://codecov.io/gh/selfrefactor/rambda/branch/master/graph/badge.svg)](https://codecov.io/gh/selfrefactor/rambda)
![Commit activity](https://img.shields.io/github/commit-activity/y/selfrefactor/rambda)
![Library size](https://img.shields.io/bundlephobia/minzip/rambda)
[![install size](https://packagephobia.com/badge?p=rambda)](https://packagephobia.com/result?p=rambda)
[![PR's Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](https://github.com/selfrefactor/rambda/pulls)
[![GitHub contributors](https://img.shields.io/github/contributors/selfrefactor/rambda.svg)](https://github.com/selfrefactor/rambda/graphs/contributors)

## ❯ Example use

```javascript
import { compose, map, filter } from 'rambda'

const result = compose(
map(x => x * 2),
filter(x => x > 2)
)([1, 2, 3, 4])
// => [6, 8]
```

You can test this example in Rambda's REPL

* [Differences between Rambda and Ramda](#differences-between-rambda-and-ramda)
* [API](#api)
* [Changelog](#-changelog)

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-example-use)

## ❯ Rambda's advantages

### TypeScript included

TypeScript definitions are included in the library, in comparison to **Ramda**, where you need to additionally install `@types/ramda`.

Still, you need to be aware that functional programming features in `TypeScript` are in development, which means that using **R.compose/R.pipe** can be problematic.

Important - Rambda version `7.1.0`(or higher) requires TypeScript version `4.3.3`(or higher).

### Understandable source code due to little usage of internals

`Ramda` uses a lot of internals, which hides a lot of logic. Reading the full source code of a method can be challenging.

### Better VSCode experience

If the project is written in Javascript, then `go to source definition` action will lead you to actual implementation of the method.

### Immutable TS definitions

You can use immutable version of Rambda definitions, which is linted with ESLint `functional/prefer-readonly-type` plugin.

```
import {add} from 'rambda/immutable'
```

### Deno support

Latest version of **Ramba** available for `Deno` users is 3 years old. This is not the case with **Rambda** as most of recent releases are available for `Deno` users.

Also, `Rambda` provides you with included TS definitions:

```
// Deno extension(https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)
// is installed and initialized
import * as R from "https://deno.land/x/rambda/mod.ts";
import * as Ramda from "https://deno.land/x/ramda/mod.ts";

R.add(1)('foo') // => will trigger warning in VSCode as it should
Ramda.add(1)('foo') // => will not trigger warning in VSCode
```

### Dot notation for `R.path`, `R.paths`, `R.assocPath` and `R.lensPath`

Standard usage of `R.path` is `R.path(['a', 'b'], {a: {b: 1} })`.

In **Rambda** you have the choice to use dot notation(which is arguably more readable):

```
R.path('a.b', {a: {b: 1} })
```

Please note that since path input is turned into array, i.e. if you want `R.path(['a','1', 'b'], {a: {'1': {b: 2}}})` to return `2`, you will have to pass array path, not string path. If you pass `a.1.b`, it will turn path input to `['a', 1, 'b']`.
The other side effect is in `R.assocPath` and `R.dissocPath`, where inputs such as `['a', '1', 'b']` will be turned into `['a', 1, 'b']`.

### Comma notation for `R.pick` and `R.omit`

Similar to dot notation, but the separator is comma(`,`) instead of dot(`.`).

```
R.pick('a,b', {a: 1 , b: 2, c: 3} })
// No space allowed between properties
```

### Speed

**Rambda** is generally more performant than `Ramda` as the [benchmarks](#-benchmarks) can prove that.

### Support

One of the main issues with `Ramda` is the slow process of releasing new versions. This is not the case with **Rambda** as releases are made on regular basis.

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-rambdas-advantages)

## ❯ Missing Ramda methods

Click to see the full list of 46 Ramda methods not implemented in Rambda and their status.

- construct - Using classes is not very functional programming oriented.
- constructN - same as above
- into - no support for transducer as it is overly complex to implement, understand and read.
- invert - overly complicated and limited use case
- invertObj
- invoker
- keysIn - we shouldn't encourage extending object with `.prototype`
- lift
- liftN
- mapAccum - `Ramda` example doesn't looks convincing
- mapAccumRight
- memoizeWith - hard to imagine its usage in context of `R.pipe`/`R.compose`
- mergeDeepWith - limited use case
- mergeDeepWithKey
- mergeWithKey
- nAry - hard to argument about and hard to create meaningful TypeScript definitions
- nthArg - limited use case
- o - enough TypeScript issues with `R.pipe`/`R.compose` to add more composition methods
- otherwise - naming is confusing
- pair - `left-pad` types of debacles happens partially because of such methods that should not be hidden, bur rather part of your code base even if they need to exist.
- partialRight - I dislike `R.partial`, so I don't want to add more methods that are based on it
- pipeWith
- project - naming is confusing, but also limited use case
- promap
- reduceRight - I find `right/left` methods confusing so I added them only where it makes sense.
- reduceWhile - functions with 4 inputs - I think that even 3 is too much
- reduced
- remove - nice name but it is too generic. Also, `Rambdax` has such method and there it works very differently
- scan - hard to explain
- sequence
- splitWhenever
- symmetricDifferenceWith
- andThen
- toPairsIn
- transduce - currently is out of focus
- traverse - same as above
- unary
- uncurryN
- unfold - similar to `R.scan` and I find that it doesn't help with readability
- unionWith - why it has its usage, I want to limit number of methods that accept more than 2 arguments
- until
- useWith - hard to explain
- valuesIn
- xprod - limited use case
- thunkify
- __ - placeholder method allows user to further customize the method call. While, it seems useful initially, the price is too high in terms of complexity for TypeScript definitions. If it is not easy exressable in TypeScript, it is not worth it as **Rambda** is a TypeScript first library.

The following methods are not going to be added(reason for exclusion is provided as a comment):

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-missing-ramda-methods)

## ❯ Install

- **yarn add rambda**

- For UMD usage either use `./dist/rambda.umd.js` or the following CDN link:

```
https://unpkg.com/rambda@CURRENT_VERSION/dist/rambda.umd.js
```

- with deno

```
import {add} from "https://deno.land/x/rambda/mod.ts";
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-install)

## Differences between Rambda and Ramda

- Rambda's **type** detects async functions and unresolved `Promises`. The returned values are `'Async'` and `'Promise'`.

- Rambda's **type** handles *NaN* input, in which case it returns `NaN`.

- Rambda's **forEach** can iterate over objects not only arrays.

- Rambda's **map**, **filter**, **partition** when they iterate over objects, they pass property and input object as predicate's argument.

- Rambda's **filter** returns empty array with bad input(`null` or `undefined`), while Ramda throws.

- Ramda's **clamp** work with strings, while Rambda's method work only with numbers.

- Ramda's **indexOf/lastIndexOf** work with strings and lists, while Rambda's method work only with lists as iterable input.

- Error handling, when wrong inputs are provided, may not be the same. This difference will be better documented once all brute force tests are completed.

- TypeScript definitions between `rambda` and `@types/ramda` may vary.

> If you need more **Ramda** methods in **Rambda**, you may either submit a `PR` or check the extended version of **Rambda** - [Rambdax](https://github.com/selfrefactor/rambdax). In case of the former, you may want to consult with [Rambda contribution guidelines.](CONTRIBUTING.md)

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-differences-between-rambda-and-ramda)

## ❯ Benchmarks

Click to expand all benchmark results

There are methods which are benchmarked only with `Ramda` and `Rambda`(i.e. no `Lodash`).

Note that some of these methods, are called with and without curring. This is done in order to give more detailed performance feedback.

The benchmarks results are produced from latest versions of *Rambda*, *Lodash*(4.17.21) and *Ramda*(0.30.1).

method | Rambda | Ramda | Lodash
--- |--- | --- | ---
*add* | 🚀 Fastest | 21.52% slower | 82.15% slower
*adjust* | 8.48% slower | 🚀 Fastest | 🔳
*all* | 🚀 Fastest | 7.18% slower | 🔳
*allPass* | 🚀 Fastest | 88.25% slower | 🔳
*allPass* | 🚀 Fastest | 98.56% slower | 🔳
*and* | 🚀 Fastest | 89.09% slower | 🔳
*any* | 🚀 Fastest | 92.87% slower | 45.82% slower
*anyPass* | 🚀 Fastest | 98.25% slower | 🔳
*append* | 🚀 Fastest | 2.07% slower | 🔳
*applySpec* | 🚀 Fastest | 80.43% slower | 🔳
*assoc* | 72.32% slower | 60.08% slower | 🚀 Fastest
*clone* | 🚀 Fastest | 91.86% slower | 86.48% slower
*compose* | 6.07% slower | 16.89% slower | 🚀 Fastest
*converge* | 78.63% slower | 🚀 Fastest | 🔳
*curry* | 🚀 Fastest | 28.86% slower | 🔳
*curryN* | 🚀 Fastest | 41.05% slower | 🔳
*defaultTo* | 🚀 Fastest | 48.91% slower | 🔳
*drop* | 🚀 Fastest | 82.35% slower | 🔳
*dropLast* | 🚀 Fastest | 86.74% slower | 🔳
*equals* | 58.37% slower | 96.73% slower | 🚀 Fastest
*filter* | 6.7% slower | 72.03% slower | 🚀 Fastest
*find* | 🚀 Fastest | 85.14% slower | 42.65% slower
*findIndex* | 🚀 Fastest | 86.48% slower | 72.27% slower
*flatten* | 🚀 Fastest | 85.68% slower | 3.57% slower
*ifElse* | 🚀 Fastest | 58.56% slower | 🔳
*includes* | 🚀 Fastest | 81.64% slower | 🔳
*indexOf* | 🚀 Fastest | 80.17% slower | 🔳
*indexOf* | 🚀 Fastest | 82.2% slower | 🔳
*init* | 🚀 Fastest | 92.24% slower | 13.3% slower
*is* | 🚀 Fastest | 57.69% slower | 🔳
*isEmpty* | 🚀 Fastest | 97.14% slower | 54.99% slower
*last* | 🚀 Fastest | 93.43% slower | 5.28% slower
*lastIndexOf* | 🚀 Fastest | 85.19% slower | 🔳
*map* | 🚀 Fastest | 86.6% slower | 11.73% slower
*match* | 🚀 Fastest | 44.83% slower | 🔳
*merge* | 🚀 Fastest | 12.21% slower | 55.76% slower
*none* | 🚀 Fastest | 96.48% slower | 🔳
*objOf* | 🚀 Fastest | 38.05% slower | 🔳
*omit* | 🚀 Fastest | 69.95% slower | 97.34% slower
*over* | 🚀 Fastest | 56.23% slower | 🔳
*path* | 37.81% slower | 77.81% slower | 🚀 Fastest
*pick* | 🚀 Fastest | 19.07% slower | 80.2% slower
*pipe* | 🚀 Fastest | 0.11% slower | 🔳
*prop* | 🚀 Fastest | 87.95% slower | 🔳
*propEq* | 🚀 Fastest | 91.92% slower | 🔳
*range* | 🚀 Fastest | 61.8% slower | 57.44% slower
*reduce* | 60.48% slower | 77.1% slower | 🚀 Fastest
*repeat* | 48.57% slower | 68.98% slower | 🚀 Fastest
*replace* | 33.45% slower | 33.99% slower | 🚀 Fastest
*set* | 🚀 Fastest | 50.35% slower | 🔳
*sort* | 🚀 Fastest | 40.23% slower | 🔳
*sortBy* | 🚀 Fastest | 25.29% slower | 56.88% slower
*split* | 🚀 Fastest | 55.37% slower | 17.64% slower
*splitEvery* | 🚀 Fastest | 71.98% slower | 🔳
*take* | 🚀 Fastest | 91.96% slower | 4.72% slower
*takeLast* | 🚀 Fastest | 93.39% slower | 19.22% slower
*test* | 🚀 Fastest | 82.34% slower | 🔳
*type* | 🚀 Fastest | 48.6% slower | 🔳
*uniq* | 🚀 Fastest | 84.9% slower | 🔳
*uniqBy* | 51.93% slower | 🚀 Fastest | 🔳
*uniqWith* | 8.29% slower | 🚀 Fastest | 🔳
*uniqWith* | 14.23% slower | 🚀 Fastest | 🔳
*update* | 🚀 Fastest | 52.35% slower | 🔳
*view* | 🚀 Fastest | 76.15% slower | 🔳

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-benchmarks)

## ❯ Used by

- [ESLint plugin Mocha](https://www.npmjs.com/package/eslint-plugin-mocha)

- [WatermelonDB](https://github.com/Nozbe/WatermelonDB)

- [Walmart Canada](https://www.walmart.ca) reported by [w-b-dev](https://github.com/w-b-dev)

- [VSCode Slack integration](https://github.com/verydanny/vcslack)

- [Webpack PostCSS](https://github.com/sectsect/webpack-postcss)

- [MobX-State-Tree decorators](https://github.com/farwayer/mst-decorators)

- [Rewrite of the Betaflight configurator](https://github.com/freshollie/fresh-configurator)

- [MineFlayer plugin](https://github.com/G07cha/MineflayerArmorManager)

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#-used-by)

## API

### add

```typescript

add(a: number, b: number): number
```

It adds `a` and `b`.

> :boom: It doesn't work with strings, as the inputs are parsed to numbers before calculation.

```javascript
R.add(2, 3) // => 5
```

All TypeScript definitions

```typescript
add(a: number, b: number): number;
add(a: number): (b: number) => number;
```

R.add source

```javascript
export function add(a, b){
if (arguments.length === 1) return _b => add(a, _b)

return Number(a) + Number(b)
}
```

Tests

```javascript
import { add as addRamda } from 'ramda'

import { compareCombinations } from './_internals/testUtils.js'
import { add } from './add.js'

test('with number', () => {
expect(add(2, 3)).toBe(5)
expect(add(7)(10)).toBe(17)
})

test('string is bad input', () => {
expect(add('foo', 'bar')).toBeNaN()
})

test('ramda specs', () => {
expect(add('1', '2')).toBe(3)
expect(add(1, '2')).toBe(3)
expect(add(true, false)).toBe(1)
expect(add(null, null)).toBe(0)
expect(add(undefined, undefined)).toBeNaN()
expect(add(new Date(1), new Date(2))).toBe(3)
})

const possibleInputs = [
/foo/,
'foo',
true,
3,
NaN,
4,
[],
Promise.resolve(1),
]

describe('brute force', () => {
compareCombinations({
fn : add,
fnRamda : addRamda,
firstInput : possibleInputs,
secondInput : possibleInputs,
callback : errorsCounters => {
expect(errorsCounters).toMatchInlineSnapshot(`
{
"ERRORS_MESSAGE_MISMATCH": 0,
"ERRORS_TYPE_MISMATCH": 0,
"RESULTS_MISMATCH": 0,
"SHOULD_NOT_THROW": 0,
"SHOULD_THROW": 0,
"TOTAL_TESTS": 64,
}
`)
},
})
})
```

TypeScript test

```typescript
import {add} from 'rambda'

describe('R.add', () => {
it('happy', () => {
const result = add(4, 1)

result // $ExpectType number
})
it('curried', () => {
const result = add(4)(1)

result // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#add)

### addIndex

```typescript

addIndex(originalFn: any): (fn: any) => (list: any[]) => any[]
```

```javascript
const result = R.addIndex(R.map)((val, idx) => val + idx + 1, [1, 2, 3])
// => [2, 4, 6]
```

All TypeScript definitions

```typescript
addIndex(originalFn: any): (fn: any) => (list: any[]) => any[];
addIndex(originalFn: any): (fn: any, list: any[]) => any[];
```

R.addIndex source

```javascript
import { _concat } from './_internals/utils.js'
import { curryN } from './curryN.js'

export function addIndex(
originalFunction,
initialIndexFn = () => 0,
loopIndexChange = x => x + 1
){
return curryN(originalFunction.length, function (){
const origFn = arguments[ 0 ]
const list = arguments[ arguments.length - 1 ]
let idx = initialIndexFn(list.length)
const args = Array.prototype.slice.call(arguments, 0)
args[ 0 ] = function (){
const result = origFn.apply(this, _concat(arguments, [ idx, list ]))
idx = loopIndexChange(idx)

return result
}

return originalFunction.apply(this, args)
})
}
```

Tests

```javascript
import * as R from 'ramda'

import { addIndex } from './addIndex.js'
import { map } from './map.js'

test('with R.pipe', () => {
const result = R.pipe(R.addIndex(R.map)((x, i) => x + i))([ 1, 2, 3 ])
expect(result).toEqual([ 1, 3, 5 ])
})

test('happy', () => {
function mapFn(fn, list){
const willReturn = []
list.forEach(item => {
willReturn.push(fn(item))
})

return willReturn
}
const mapIndexed = addIndex(mapFn)
const fn2 = (val, idx) => val + idx + 5
const result = mapIndexed(fn2, [ 1, 2, 3 ])
expect(result).toEqual([ 6, 8, 10 ])
})

describe('unary functions like `map`', () => {
const times2 = function (x){
return x * 2
}
const addIndexParam = function (x, idx){
return x + idx
}
const squareEnds = function (
x, idx, list
){
return idx === 0 || idx === list.length - 1 ? x * x : x
}
const mapIndexed = addIndex(map)

it('works just like a normal map', () => {
expect(mapIndexed(times2, [ 1, 2, 3, 4 ])).toEqual([ 2, 4, 6, 8 ])
})

it('passes the index as a second parameter to the callback', () => {
expect(mapIndexed(addIndexParam, [ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([
8, 7, 9, 8, 7, 5, 15,
])
})

it('passes the entire list as a third parameter to the callback', () => {
expect(mapIndexed(squareEnds, [ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([
64, 6, 7, 5, 3, 0, 81,
])
})

it('acts as a curried function', () => {
const makeSquareEnds = mapIndexed(squareEnds)
expect(makeSquareEnds([ 8, 6, 7, 5, 3, 0, 9 ])).toEqual([
64, 6, 7, 5, 3, 0, 81,
])
})
})
```

TypeScript test

```typescript
import {addIndex, map, pipe} from 'rambda'

describe('R.addIndex', () => {
it('happy', () => {
function mapFn(fn: (x: T) => T, list: T[]) {
const willReturn: T[] = []
list.forEach(item => {
willReturn.push(fn(item))
})

return willReturn
}
const mapIndexed = addIndex(mapFn)
const fn = (val: number, idx: number, list: number[]) =>
val + idx + 5 + list[0]
const result = mapIndexed(fn)([1, 2, 3])
result // $ExpectType any[]
})
it('with pipe', () => {
const result = pipe(
addIndex(map)((x: number, i: number) => {
return x + i
})
)([1, 2, 3])
result // $ExpectType any[]
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addIndex)

### addIndexRight

```typescript

addIndexRight(originalFn: any): (fn: any) => (list: any[]) => any[]
```

Same as `R.addIndex`, but it will passed indexes are decreasing, instead of increasing.

All TypeScript definitions

```typescript
addIndexRight(originalFn: any): (fn: any) => (list: any[]) => any[];
addIndexRight(originalFn: any): (fn: any, list: any[]) => any[];
```

R.addIndexRight source

```javascript
import { addIndex } from './addIndex.js'

export function addIndexRight(originalFunction){
return addIndex(
originalFunction,
listLength => listLength - 1,
x => x - 1
)
}
```

Tests

```javascript
import { addIndexRight } from './addIndexRight.js'
import { map } from './map.js'

test('happy', () => {
function mapFn(fn, list){
const willReturn = []
list.forEach(item => {
willReturn.push(fn(item))
})

return willReturn
}
const mapIndexed = addIndexRight(mapFn)
const fn2 = (val, idx) => val + idx + 5
expect(mapIndexed(fn2, [ 1, 2, 3 ])).toEqual([ 8, 8, 8 ])
const revmap = (fn, ary) => map(fn, ary)
const revmapIndexed = addIndexRight(revmap)
const result = revmapIndexed((val, idx) => idx + '-' + val,
[ 'f', 'o', 'o', 'b', 'a', 'r' ])

expect(result).toEqual([ '5-f', '4-o', '3-o', '2-b', '1-a', '0-r' ])
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#addIndexRight)

### adjust

```typescript

adjust(index: number, replaceFn: (x: T) => T, list: T[]): T[]
```

It replaces `index` in array `list` with the result of `replaceFn(list[i])`.

```javascript
const result = R.adjust(
0,
a => a + 1,
[0, 100]
) // => [1, 100]
```

All TypeScript definitions

```typescript
adjust(index: number, replaceFn: (x: T) => T, list: T[]): T[];
adjust(index: number, replaceFn: (x: T) => T): (list: T[]) => T[];
```

R.adjust source

```javascript
import { cloneList } from './_internals/cloneList.js'
import { curry } from './curry.js'

function adjustFn(
index, replaceFn, list
){
const actualIndex = index < 0 ? list.length + index : index
if (index >= list.length || actualIndex < 0) return list

const clone = cloneList(list)
clone[ actualIndex ] = replaceFn(clone[ actualIndex ])

return clone
}

export const adjust = curry(adjustFn)
```

Tests

```javascript
import { add } from './add.js'
import { adjust } from './adjust.js'
import { pipe } from './pipe.js'

const list = [ 0, 1, 2 ]
const expected = [ 0, 11, 2 ]

test('happy', () => {})

test('happy', () => {
expect(adjust(
1, add(10), list
)).toEqual(expected)
})

test('with curring type 1 1 1', () => {
expect(adjust(1)(add(10))(list)).toEqual(expected)
})

test('with curring type 1 2', () => {
expect(adjust(1)(add(10), list)).toEqual(expected)
})

test('with curring type 2 1', () => {
expect(adjust(1, add(10))(list)).toEqual(expected)
})

test('with negative index', () => {
expect(adjust(
-2, add(10), list
)).toEqual(expected)
})

test('when index is out of bounds', () => {
const list = [ 0, 1, 2, 3 ]
expect(adjust(
4, add(1), list
)).toEqual(list)
expect(adjust(
-5, add(1), list
)).toEqual(list)
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#adjust)

### all

```typescript

all(predicate: (x: T) => boolean, list: T[]): boolean
```

It returns `true`, if all members of array `list` returns `true`, when applied as argument to `predicate` function.

```javascript
const list = [ 0, 1, 2, 3, 4 ]
const predicate = x => x > -1

const result = R.all(predicate, list)
// => true
```

All TypeScript definitions

```typescript
all(predicate: (x: T) => boolean, list: T[]): boolean;
all(predicate: (x: T) => boolean): (list: T[]) => boolean;
```

R.all source

```javascript
export function all(predicate, list){
if (arguments.length === 1) return _list => all(predicate, _list)

for (let i = 0; i < list.length; i++){
if (!predicate(list[ i ])) return false
}

return true
}
```

Tests

```javascript
import { all } from './all.js'

const list = [ 0, 1, 2, 3, 4 ]

test('when true', () => {
const fn = x => x > -1

expect(all(fn)(list)).toBeTrue()
})

test('when false', () => {
const fn = x => x > 2

expect(all(fn, list)).toBeFalse()
})
```

TypeScript test

```typescript
import {all} from 'rambda'

describe('all', () => {
it('happy', () => {
const result = all(
x => {
x // $ExpectType number
return x > 0
},
[1, 2, 3]
)
result // $ExpectType boolean
})
it('curried needs a type', () => {
const result = all(x => {
x // $ExpectType number
return x > 0
})([1, 2, 3])
result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#all)

### allPass

```typescript

allPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean
```

It returns `true`, if all functions of `predicates` return `true`, when `input` is their argument.

```javascript
const input = {
a : 1,
b : 2,
}
const predicates = [
x => x.a === 1,
x => x.b === 2,
]
const result = R.allPass(predicates)(input) // => true
```

All TypeScript definitions

```typescript
allPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean;
allPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean;
```

R.allPass source

```javascript
export function allPass(predicates){
return (...input) => {
let counter = 0
while (counter < predicates.length){
if (!predicates[ counter ](...input)){
return false
}
counter++
}

return true
}
}
```

Tests

```javascript
import { allPass } from './allPass.js'

test('happy', () => {
const rules = [ x => typeof x === 'number', x => x > 10, x => x * 7 < 100 ]

expect(allPass(rules)(11)).toBeTrue()

expect(allPass(rules)(undefined)).toBeFalse()
})

test('when returns true', () => {
const conditionArr = [ val => val.a === 1, val => val.b === 2 ]

expect(allPass(conditionArr)({
a : 1,
b : 2,
})).toBeTrue()
})

test('when returns false', () => {
const conditionArr = [ val => val.a === 1, val => val.b === 3 ]

expect(allPass(conditionArr)({
a : 1,
b : 2,
})).toBeFalse()
})

test('works with multiple inputs', () => {
const fn = function (
w, x, y, z
){
return w + x === y + z
}
expect(allPass([ fn ])(
3, 3, 3, 3
)).toBeTrue()
})
```

TypeScript test

```typescript
import {allPass, filter} from 'rambda'

describe('allPass', () => {
it('happy', () => {
const x = allPass([
y => {
y // $ExpectType number
return typeof y === 'number'
},
y => {
return y > 0
},
])(11)

x // $ExpectType boolean
})
it('issue #642', () => {
const isGreater = (num: number) => num > 5
const pred = allPass([isGreater])
const xs = [0, 1, 2, 3]

const filtered1 = filter(pred)(xs)
filtered1 // $ExpectType number[]
const filtered2 = xs.filter(pred)
filtered2 // $ExpectType number[]
})
it('issue #604', () => {
const plusEq = function(w: number, x: number, y: number, z: number) {
return w + x === y + z
}
const result = allPass([plusEq])(3, 3, 3, 3)

result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#allPass)

### always

```typescript

always(x: T): (...args: unknown[]) => T
```

It returns function that always returns `x`.

```javascript
const fn = R.always(7)

const result = fn()
// => 7
```

All TypeScript definitions

```typescript
always(x: T): (...args: unknown[]) => T;
```

R.always source

```javascript
export function always(x){
return _ => x
}
```

Tests

```javascript
import { always } from './always.js'
import { applySpec } from './applySpec.js'

test('happy', () => {
const fn = always(7)

expect(fn()).toBe(7)
expect(fn()).toBe(7)
})

test('compatibility with applySpec', () => {
const spec = applySpec({ x : always('foo') })
expect(spec({})).toEqual({ x : 'foo' })
})
```

TypeScript test

```typescript
import {always} from 'rambda'

describe('R.always', () => {
it('happy', () => {
const fn = always('foo')
fn // $ExpectType (...args: unknown[]) => string
const result = fn()
result // $ExpectType string
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#always)

### and

```typescript

and(x: T, y: U): T | U
```

Logical AND

```javascript
R.and(true, true); // => true
R.and(false, true); // => false
R.and(true, 'foo'); // => 'foo'
```

All TypeScript definitions

```typescript
and(x: T, y: U): T | U;
and(x: T): (y: U) => T | U;
```

R.and source

```javascript
export function and(a, b){
if (arguments.length === 1) return _b => and(a, _b)

return a && b
}
```

Tests

```javascript
import { and } from './and.js'

test('happy', () => {
expect(and(1, 'foo')).toBe('foo')
expect(and(true, true)).toBeTrue()
expect(and(true)(true)).toBeTrue()
expect(and(true, false)).toBeFalse()
expect(and(false, true)).toBeFalse()
expect(and(false, false)).toBeFalse()
})
```

TypeScript test

```typescript
import {and} from 'rambda'

describe('R.and', () => {
it('happy', () => {
const result = and(true, false)
result // $ExpectType boolean
})
it('curried', () => {
const result = and('foo')(1)
result // $ExpectType string | 1
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#and)

### any

```typescript

any(predicate: (x: T) => boolean, list: T[]): boolean
```

It returns `true`, if at least one member of `list` returns true, when passed to a `predicate` function.

```javascript
const list = [1, 2, 3]
const predicate = x => x * x > 8
R.any(fn, list)
// => true
```

All TypeScript definitions

```typescript
any(predicate: (x: T) => boolean, list: T[]): boolean;
any(predicate: (x: T) => boolean): (list: T[]) => boolean;
```

R.any source

```javascript
export function any(predicate, list){
if (arguments.length === 1) return _list => any(predicate, _list)

let counter = 0
while (counter < list.length){
if (predicate(list[ counter ], counter)){
return true
}
counter++
}

return false
}
```

Tests

```javascript
import { any } from './any.js'

const list = [ 1, 2, 3 ]

test('happy', () => {
expect(any(x => x < 0, list)).toBeFalse()
})

test('with curry', () => {
expect(any(x => x > 2)(list)).toBeTrue()
})
```

TypeScript test

```typescript
import {any} from 'rambda'

describe('R.any', () => {
it('happy', () => {
const result = any(
x => {
x // $ExpectType number
return x > 2
},
[1, 2, 3]
)
result // $ExpectType boolean
})

it('when curried needs a type', () => {
const result = any(x => {
x // $ExpectType number
return x > 2
})([1, 2, 3])
result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#any)

### anyPass

```typescript

anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean
```

It accepts list of `predicates` and returns a function. This function with its `input` will return `true`, if any of `predicates` returns `true` for this `input`.

```javascript
const isBig = x => x > 20
const isOdd = x => x % 2 === 1
const input = 11

const fn = R.anyPass(
[isBig, isOdd]
)

const result = fn(input)
// => true
```

All TypeScript definitions

```typescript
anyPass(predicates: ((x: T) => boolean)[]): (input: T) => boolean;
anyPass(predicates: ((...inputs: T[]) => boolean)[]): (...inputs: T[]) => boolean;
```

R.anyPass source

```javascript
export function anyPass(predicates){
return (...input) => {
let counter = 0
while (counter < predicates.length){
if (predicates[ counter ](...input)){
return true
}
counter++
}

return false
}
}
```

Tests

```javascript
import { anyPass } from './anyPass.js'

test('happy', () => {
const rules = [ x => typeof x === 'string', x => x > 10 ]
const predicate = anyPass(rules)
expect(predicate('foo')).toBeTrue()
expect(predicate(6)).toBeFalse()
})

test('happy', () => {
const rules = [ x => typeof x === 'string', x => x > 10 ]

expect(anyPass(rules)(11)).toBeTrue()
expect(anyPass(rules)(undefined)).toBeFalse()
})

const obj = {
a : 1,
b : 2,
}

test('when returns true', () => {
const conditionArr = [ val => val.a === 1, val => val.a === 2 ]

expect(anyPass(conditionArr)(obj)).toBeTrue()
})

test('when returns false + curry', () => {
const conditionArr = [ val => val.a === 2, val => val.b === 3 ]

expect(anyPass(conditionArr)(obj)).toBeFalse()
})

test('with empty predicates list', () => {
expect(anyPass([])(3)).toBeFalse()
})

test('works with multiple inputs', () => {
const fn = function (
w, x, y, z
){
console.log(
w, x, y, z
)

return w + x === y + z
}
expect(anyPass([ fn ])(
3, 3, 3, 3
)).toBeTrue()
})
```

TypeScript test

```typescript
import {anyPass, filter} from 'rambda'

describe('anyPass', () => {
it('happy', () => {
const x = anyPass([
y => {
y // $ExpectType number
return typeof y === 'number'
},
y => {
return y > 0
},
])(11)

x // $ExpectType boolean
})
it('issue #604', () => {
const plusEq = function(w: number, x: number, y: number, z: number) {
return w + x === y + z
}
const result = anyPass([plusEq])(3, 3, 3, 3)

result // $ExpectType boolean
})
it('issue #642', () => {
const isGreater = (num: number) => num > 5
const pred = anyPass([isGreater])
const xs = [0, 1, 2, 3]

const filtered1 = filter(pred)(xs)
filtered1 // $ExpectType number[]
const filtered2 = xs.filter(pred)
filtered2 // $ExpectType number[]
})
it('functions as a type guard', () => {
const isString = (x: unknown): x is string => typeof x === 'string'
const isNumber = (x: unknown): x is number => typeof x === 'number'
const isBoolean = (x: unknown): x is boolean => typeof x === 'boolean'

const isStringNumberOrBoolean = anyPass([isString, isNumber, isBoolean])

isStringNumberOrBoolean // $ExpectType (input: unknown) => boolean

const aValue: unknown = 1

if (isStringNumberOrBoolean(aValue)) {
aValue // $ExpectType unknown
}
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#anyPass)

### ap

```typescript

ap(fns: Array<(a: T) => U>[], vs: T[]): U[]
```

It takes a list of functions and a list of values. Then it returns a list of values obtained by applying each function to each value.

```javascript
const result = R.ap(
[
x => x + 1,
x => x + 2,
],
[1, 2, 3]
)
// => [2, 3, 4, 3, 4, 5]
```

All TypeScript definitions

```typescript
ap(fns: Array<(a: T) => U>[], vs: T[]): U[];
ap(fns: Array<(a: T) => U>): (vs: T[]) => U[];
ap(fn: (r: R, a: A) => B, fn1: (r: R) => A): (r: R) => B;
```

R.ap source

```javascript
export function ap(functions, input){
if (arguments.length === 1){
return _inputs => ap(functions, _inputs)
}

return functions.reduce((acc, fn) => [ ...acc, ...input.map(fn) ], [])
}
```

Tests

```javascript
import { ap } from './ap.js'

function mult2(x){
return x * 2
}
function plus3(x){
return x + 3
}

test('happy', () => {
expect(ap([ mult2, plus3 ], [ 1, 2, 3 ])).toEqual([ 2, 4, 6, 4, 5, 6 ])
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ap)

### aperture

```typescript

aperture(n: N, list: T[]): Array> | []
```

It returns a new list, composed of consecutive `n`-tuples from a `list`.

```javascript
const result = R.aperture(2, [1, 2, 3, 4])
// => [[1, 2], [2, 3], [3, 4]]
```

All TypeScript definitions

```typescript
aperture(n: N, list: T[]): Array> | [];
aperture(n: N): (list: T[]) => Array> | [];
```

R.aperture source

```javascript
export function aperture(step, list){
if (arguments.length === 1){
return _list => aperture(step, _list)
}
if (step > list.length) return []
let idx = 0
const limit = list.length - (step - 1)
const acc = new Array(limit)
while (idx < limit){
acc[ idx ] = list.slice(idx, idx + step)
idx += 1
}

return acc
}
```

Tests

```javascript
import { aperture } from './aperture.js'

const list = [ 1, 2, 3, 4, 5, 6, 7 ]

test('happy', () => {
expect(aperture(1, list)).toEqual([ [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ] ])
expect(aperture(2, list)).toEqual([
[ 1, 2 ],
[ 2, 3 ],
[ 3, 4 ],
[ 4, 5 ],
[ 5, 6 ],
[ 6, 7 ],
])
expect(aperture(3, list)).toEqual([
[ 1, 2, 3 ],
[ 2, 3, 4 ],
[ 3, 4, 5 ],
[ 4, 5, 6 ],
[ 5, 6, 7 ],
])
expect(aperture(8, list)).toEqual([])
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#aperture)

### append

```typescript

append(xToAppend: T, iterable: T[]): T[]
```

It adds element `x` at the end of `iterable`.

```javascript
const x = 'foo'

const result = R.append(x, ['bar', 'baz'])
// => ['bar', 'baz', 'foo']
```

All TypeScript definitions

```typescript
append(xToAppend: T, iterable: T[]): T[];
append(xToAppend: T, iterable: IsFirstSubtypeOfSecond[]) : U[];
append(xToAppend: T): (iterable: IsFirstSubtypeOfSecond[]) => U[];
append(xToAppend: T): (iterable: T[]) => T[];
```

R.append source

```javascript
import { cloneList } from './_internals/cloneList.js'

export function append(x, input){
if (arguments.length === 1) return _input => append(x, _input)

if (typeof input === 'string') return input.split('').concat(x)

const clone = cloneList(input)
clone.push(x)

return clone
}
```

Tests

```javascript
import { append } from './append.js'

test('happy', () => {
expect(append('tests', [ 'write', 'more' ])).toEqual([
'write',
'more',
'tests',
])
})

test('append to empty array', () => {
expect(append('tests')([])).toEqual([ 'tests' ])
})

test('with strings', () => {
expect(append('o', 'fo')).toEqual([ 'f', 'o', 'o' ])
})
```

TypeScript test

```typescript
import {append, prepend} from 'rambda'

const listOfNumbers = [1, 2, 3]
const listOfNumbersAndStrings = [1, 'b', 3]

describe('R.append/R.prepend', () => {
describe("with the same primitive type as the array's elements", () => {
it('uncurried', () => {
// @ts-expect-error
append('d', listOfNumbers)
// @ts-expect-error
prepend('d', listOfNumbers)
append(4, listOfNumbers) // $ExpectType number[]
prepend(4, listOfNumbers) // $ExpectType number[]
})

it('curried', () => {
// @ts-expect-error
append('d')(listOfNumbers)
append(4)(listOfNumbers) // $ExpectType number[]
prepend(4)(listOfNumbers) // $ExpectType number[]
})
})

describe("with a subtype of the array's elements", () => {
it('uncurried', () => {
// @ts-expect-error
append(true, listOfNumbersAndStrings)
append(4, listOfNumbersAndStrings) // $ExpectType (string | number)[]
prepend(4, listOfNumbersAndStrings) // $ExpectType (string | number)[]
})

it('curried', () => {
// @ts-expect-error
append(true)(listOfNumbersAndStrings)
append(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[]
prepend(4)(listOfNumbersAndStrings) // $ExpectType (string | number)[]
})
})

describe("expanding the type of the array's elements", () => {
it('uncurried', () => {
// @ts-expect-error
append('d', listOfNumbers)
append('d', listOfNumbers) // $ExpectType (string | number)[]
prepend('d', listOfNumbers) // $ExpectType (string | number)[]
})

it('curried', () => {
// @ts-expect-error
append('d')(listOfNumbers)
const appendD = append('d')
appendD(listOfNumbers) // $ExpectType (string | number)[]
const prependD = prepend('d')
prependD(listOfNumbers) // $ExpectType (string | number)[]
})
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#append)

### apply

```typescript

apply(fn: (...args: any[]) => T, args: any[]): T
```

It applies function `fn` to the list of arguments.

This is useful for creating a fixed-arity function from a variadic function. `fn` should be a bound function if context is significant.

```javascript
const result = R.apply(Math.max, [42, -Infinity, 1337])
// => 1337
```

All TypeScript definitions

```typescript
apply(fn: (...args: any[]) => T, args: any[]): T;
apply(fn: (...args: any[]) => T): (args: any[]) => T;
```

R.apply source

```javascript
export function apply(fn, args){
if (arguments.length === 1){
return _args => apply(fn, _args)
}

return fn.apply(this, args)
}
```

Tests

```javascript
import { apply } from './apply.js'
import { bind } from './bind.js'
import { identity } from './identity.js'

test('happy', () => {
expect(apply(identity, [ 1, 2, 3 ])).toBe(1)
})

test('applies function to argument list', () => {
expect(apply(Math.max, [ 1, 2, 3, -99, 42, 6, 7 ])).toBe(42)
})

test('provides no way to specify context', () => {
const obj = {
method (){
return this === obj
},
}
expect(apply(obj.method, [])).toBeFalse()
expect(apply(bind(obj.method, obj), [])).toBeTrue()
})
```

TypeScript test

```typescript
import {apply, identity} from 'rambda'

describe('R.apply', () => {
it('happy', () => {
const result = apply(identity, [1, 2, 3])

result // $ExpectType number
})
it('curried', () => {
const fn = apply(identity)
const result = fn([1, 2, 3])

result // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#apply)

### applySpec

```typescript

applySpec>(
spec: Spec
): (
...args: Parameters>
) => { [Key in keyof Spec]: ReturnType }
```

> :boom: The currying in this function works best with functions with 4 arguments or less. (arity of 4)

```javascript
const fn = R.applySpec({
sum: R.add,
nested: { mul: R.multiply }
})
const result = fn(2, 4)
// => { sum: 6, nested: { mul: 8 } }
```

All TypeScript definitions

```typescript
applySpec>(
spec: Spec
): (
...args: Parameters>
) => { [Key in keyof Spec]: ReturnType };
applySpec(spec: any): (...args: unknown[]) => T;
```

R.applySpec source

```javascript
import { isArray } from './_internals/isArray.js'

// recursively traverse the given spec object to find the highest arity function
export function __findHighestArity(spec, max = 0){
for (const key in spec){
if (spec.hasOwnProperty(key) === false || key === 'constructor') continue

if (typeof spec[ key ] === 'object'){
max = Math.max(max, __findHighestArity(spec[ key ]))
}

if (typeof spec[ key ] === 'function'){
max = Math.max(max, spec[ key ].length)
}
}

return max
}

function __filterUndefined(){
const defined = []
let i = 0
const l = arguments.length
while (i < l){
if (typeof arguments[ i ] === 'undefined') break
defined[ i ] = arguments[ i ]
i++
}

return defined
}

function __applySpecWithArity(
spec, arity, cache
){
const remaining = arity - cache.length

if (remaining === 1)
return x =>
__applySpecWithArity(
spec, arity, __filterUndefined(...cache, x)
)
if (remaining === 2)
return (x, y) =>
__applySpecWithArity(
spec, arity, __filterUndefined(
...cache, x, y
)
)
if (remaining === 3)
return (
x, y, z
) =>
__applySpecWithArity(
spec, arity, __filterUndefined(
...cache, x, y, z
)
)
if (remaining === 4)
return (
x, y, z, a
) =>
__applySpecWithArity(
spec,
arity,
__filterUndefined(
...cache, x, y, z, a
)
)
if (remaining > 4)
return (...args) =>
__applySpecWithArity(
spec, arity, __filterUndefined(...cache, ...args)
)

// handle spec as Array
if (isArray(spec)){
const ret = []
let i = 0
const l = spec.length
for (; i < l; i++){
// handle recursive spec inside array
if (typeof spec[ i ] === 'object' || isArray(spec[ i ])){
ret[ i ] = __applySpecWithArity(
spec[ i ], arity, cache
)
}
// apply spec to the key
if (typeof spec[ i ] === 'function'){
ret[ i ] = spec[ i ](...cache)
}
}

return ret
}

// handle spec as Object
const ret = {}
// apply callbacks to each property in the spec object
for (const key in spec){
if (spec.hasOwnProperty(key) === false || key === 'constructor') continue

// apply the spec recursively
if (typeof spec[ key ] === 'object'){
ret[ key ] = __applySpecWithArity(
spec[ key ], arity, cache
)
continue
}

// apply spec to the key
if (typeof spec[ key ] === 'function'){
ret[ key ] = spec[ key ](...cache)
}
}

return ret
}

export function applySpec(spec, ...args){
// get the highest arity spec function, cache the result and pass to __applySpecWithArity
const arity = __findHighestArity(spec)

if (arity === 0){
return () => ({})
}
const toReturn = __applySpecWithArity(
spec, arity, args
)

return toReturn
}
```

Tests

```javascript
import { applySpec as applySpecRamda, nAry } from 'ramda'

import {
add,
always,
compose,
dec,
inc,
map,
path,
prop,
T,
} from '../rambda.js'
import { applySpec } from './applySpec.js'

test('different than Ramda when bad spec', () => {
const result = applySpec({ sum : { a : 1 } })(1, 2)
const ramdaResult = applySpecRamda({ sum : { a : 1 } })(1, 2)
expect(result).toEqual({})
expect(ramdaResult).toEqual({ sum : { a : {} } })
})

test('works with empty spec', () => {
expect(applySpec({})()).toEqual({})
expect(applySpec([])(1, 2)).toEqual({})
expect(applySpec(null)(1, 2)).toEqual({})
})

test('works with unary functions', () => {
const result = applySpec({
v : inc,
u : dec,
})(1)
const expected = {
v : 2,
u : 0,
}
expect(result).toEqual(expected)
})

test('works with binary functions', () => {
const result = applySpec({ sum : add })(1, 2)
expect(result).toEqual({ sum : 3 })
})

test('works with nested specs', () => {
const result = applySpec({
unnested : always(0),
nested : { sum : add },
})(1, 2)
const expected = {
unnested : 0,
nested : { sum : 3 },
}
expect(result).toEqual(expected)
})

test('works with arrays of nested specs', () => {
const result = applySpec({
unnested : always(0),
nested : [ { sum : add } ],
})(1, 2)

expect(result).toEqual({
unnested : 0,
nested : [ { sum : 3 } ],
})
})

test('works with arrays of spec objects', () => {
const result = applySpec([ { sum : add } ])(1, 2)

expect(result).toEqual([ { sum : 3 } ])
})

test('works with arrays of functions', () => {
const result = applySpec([ map(prop('a')), map(prop('b')) ])([
{
a : 'a1',
b : 'b1',
},
{
a : 'a2',
b : 'b2',
},
])
const expected = [
[ 'a1', 'a2' ],
[ 'b1', 'b2' ],
]
expect(result).toEqual(expected)
})

test('works with a spec defining a map key', () => {
expect(applySpec({ map : prop('a') })({ a : 1 })).toEqual({ map : 1 })
})

test('cannot retains the highest arity', () => {
const f = applySpec({
f1 : nAry(2, T),
f2 : nAry(5, T),
})
const fRamda = applySpecRamda({
f1 : nAry(2, T),
f2 : nAry(5, T),
})
expect(f).toHaveLength(0)
expect(fRamda).toHaveLength(5)
})

test('returns a curried function', () => {
expect(applySpec({ sum : add })(1)(2)).toEqual({ sum : 3 })
})

// Additional tests
// ============================================
test('arity', () => {
const spec = {
one : x1 => x1,
two : (x1, x2) => x1 + x2,
three : (
x1, x2, x3
) => x1 + x2 + x3,
}
expect(applySpec(
spec, 1, 2, 3
)).toEqual({
one : 1,
two : 3,
three : 6,
})
})

test('arity over 5 arguments', () => {
const spec = {
one : x1 => x1,
two : (x1, x2) => x1 + x2,
three : (
x1, x2, x3
) => x1 + x2 + x3,
four : (
x1, x2, x3, x4
) => x1 + x2 + x3 + x4,
five : (
x1, x2, x3, x4, x5
) => x1 + x2 + x3 + x4 + x5,
}
expect(applySpec(
spec, 1, 2, 3, 4, 5
)).toEqual({
one : 1,
two : 3,
three : 6,
four : 10,
five : 15,
})
})

test('curried', () => {
const spec = {
one : x1 => x1,
two : (x1, x2) => x1 + x2,
three : (
x1, x2, x3
) => x1 + x2 + x3,
}
expect(applySpec(spec)(1)(2)(3)).toEqual({
one : 1,
two : 3,
three : 6,
})
})

test('curried over 5 arguments', () => {
const spec = {
one : x1 => x1,
two : (x1, x2) => x1 + x2,
three : (
x1, x2, x3
) => x1 + x2 + x3,
four : (
x1, x2, x3, x4
) => x1 + x2 + x3 + x4,
five : (
x1, x2, x3, x4, x5
) => x1 + x2 + x3 + x4 + x5,
}
expect(applySpec(spec)(1)(2)(3)(4)(5)).toEqual({
one : 1,
two : 3,
three : 6,
four : 10,
five : 15,
})
})

test('undefined property', () => {
const spec = { prop : path([ 'property', 'doesnt', 'exist' ]) }
expect(applySpec(spec, {})).toEqual({ prop : undefined })
})

test('restructure json object', () => {
const spec = {
id : path('user.id'),
name : path('user.firstname'),
profile : path('user.profile'),
doesntExist : path('user.profile.doesntExist'),
info : { views : compose(inc, prop('views')) },
type : always('playa'),
}

const data = {
user : {
id : 1337,
firstname : 'john',
lastname : 'shaft',
profile : 'shaft69',
},
views : 42,
}

expect(applySpec(spec, data)).toEqual({
id : 1337,
name : 'john',
profile : 'shaft69',
doesntExist : undefined,
info : { views : 43 },
type : 'playa',
})
})
```

TypeScript test

```typescript
import {multiply, applySpec, inc, dec, add} from 'rambda'

describe('applySpec', () => {
it('ramda 1', () => {
const result = applySpec({
v: inc,
u: dec,
})(1)
result // $ExpectType { v: number; u: number; }
})
it('ramda 1', () => {
interface Output {
sum: number,
multiplied: number,
}
const result = applySpec({
sum: add,
multiplied: multiply,
})(1, 2)

result // $ExpectType Output
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#applySpec)

### applyTo

```typescript

applyTo(el: T, fn: (t: T) => U): U
```

```javascript
const result = R.applyTo(
1,
x => x + 1
)
// => 2
```

All TypeScript definitions

```typescript
applyTo(el: T, fn: (t: T) => U): U;
applyTo(el: T): (fn: (t: T) => U) => U;
```

R.applyTo source

```javascript
export function applyTo(input, fn){
if (arguments.length === 1){
return _fn => applyTo(input, _fn)
}

return fn(input)
}
```

Tests

```javascript
import { applyTo } from './applyTo.js'
import { multiply } from './multiply.js'

test('happy', () => {
expect(applyTo(21, multiply(2))).toBe(42)
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#applyTo)

### ascend

```typescript

ascend(fn: (obj: T) => Ord, a: T, b: T): Ordering
```

```javascript
const result = R.sort(R.descend(x => x), [2, 1])
// => [1, 2]
```

All TypeScript definitions

```typescript
ascend(fn: (obj: T) => Ord, a: T, b: T): Ordering;
ascend(fn: (obj: T) => Ord): (a: T, b: T) => Ordering;
```

R.ascend source

```javascript
export function createCompareFunction(
a, b, winner, loser
){
if (a === b) return 0

return a < b ? winner : loser
}

export function ascend(
getFunction, a, b
){
if (arguments.length === 1){
return (_a, _b) => ascend(
getFunction, _a, _b
)
}
const aValue = getFunction(a)
const bValue = getFunction(b)

return createCompareFunction(
aValue, bValue, -1, 1
)
}
```

Tests

```javascript
import { ascend } from './ascend.js'
import { descend } from './descend.js'
import { sort } from './sort.js'

const people = [
{
name : 'Emma',
age : 70,
},
{
name : 'Peter',
age : 78,
},
{
name : 'Mikhail',
age : 62,
},
]

test('ascend', () => {
const result = sort(ascend(x => x?.age),
people)
const expected = [
{
name : 'Mikhail',
age : 62,
},
{
name : 'Emma',
age : 70,
},
{
name : 'Peter',
age : 78,
},
]
expect(result).toEqual(expected)
})

test('descend', () => {
const result = sort(descend(x => x?.age),
people)
const expected = [
{
name : 'Peter',
age : 78,
},
{
name : 'Emma',
age : 70,
},
{
name : 'Mikhail',
age : 62,
},
]

expect(result).toEqual(expected)
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#ascend)

### assoc

```typescript

assoc(prop: K): {
(val: T): >(obj: U) => U
```

It makes a shallow clone of `obj` with setting or overriding the property `prop` with `newValue`.

> :boom: This copies and flattens prototype properties
onto the new object as well. All non-primitive properties are copied by
reference.

```javascript
R.assoc('c', 3, {a: 1, b: 2})
// => {a: 1, b: 2, c: 3}
```

All TypeScript definitions

```typescript
assoc(prop: K): {
(val: T): >(obj: U) => U;
, T>(val: T, obj: U): U;
};
assoc(prop: K, val: T): {
(obj: U): U extends Record ? U[K] extends T ? U : Record & Omit : U & Record;
};
assoc(prop: K, val: T, obj: U): U;
```

R.assoc source

```javascript
import { curry } from './curry.js'

export function assocFn(
prop, newValue, obj
){
return Object.assign(
{}, obj, { [ prop ] : newValue }
)
}

export const assoc = curry(assocFn)
```

Tests

```javascript
import { assoc } from './assoc.js'

test('adds a key to an empty object', () => {
expect(assoc(
'a', 1, {}
)).toEqual({ a : 1 })
})

test('adds a key to a non-empty object', () => {
expect(assoc(
'b', 2, { a : 1 }
)).toEqual({
a : 1,
b : 2,
})
})

test('adds a key to a non-empty object - curry case 1', () => {
expect(assoc('b', 2)({ a : 1 })).toEqual({
a : 1,
b : 2,
})
})

test('adds a key to a non-empty object - curry case 2', () => {
expect(assoc('b')(2, { a : 1 })).toEqual({
a : 1,
b : 2,
})
})

test('adds a key to a non-empty object - curry case 3', () => {
const result = assoc('b')(2)({ a : 1 })

expect(result).toEqual({
a : 1,
b : 2,
})
})

test('changes an existing key', () => {
expect(assoc(
'a', 2, { a : 1 }
)).toEqual({ a : 2 })
})

test('undefined is considered an empty object', () => {
expect(assoc(
'a', 1, undefined
)).toEqual({ a : 1 })
})

test('null is considered an empty object', () => {
expect(assoc(
'a', 1, null
)).toEqual({ a : 1 })
})

test('value can be null', () => {
expect(assoc(
'a', null, null
)).toEqual({ a : null })
})

test('value can be undefined', () => {
expect(assoc(
'a', undefined, null
)).toEqual({ a : undefined })
})

test('assignment is shallow', () => {
expect(assoc(
'a', { b : 2 }, { a : { c : 3 } }
)).toEqual({ a : { b : 2 } })
})
```

TypeScript test

```typescript
import { assoc } from 'rambda'

type Obj = {
str: string
num: number
}

const obj: Obj = { str: 'foo', num: 1 }
const newValue = 2
const newProp = 'num'

describe('R.assoc', () => {
it('happy', () => {
const result = assoc(newProp, newValue, obj)

result.num // $ExpectType number
result.str // $ExpectType string
})
it('curried 1', () => {
const result = assoc(newProp, newValue)(obj)

result.num // $ExpectType number
result.str // $ExpectType string
})
it('curried 2', () => {
const result = assoc(newProp)(newValue)(obj)

result.num // $ExpectType number
result.str // $ExpectType string
})
it('from @types/ramda', () => {
// @ts-expect-error
assoc('str')(2, obj)
// @ts-expect-error
assoc('what')('bar', obj)

const result1 = assoc('what')(2, {} as Record)
result1.what // $ExpectType number

const result2 = assoc('str')('bar')(obj)
result2.str // $ExpectType string
result2.num // $ExpectType number

// @ts-expect-error
assoc('str')(2)(obj)
// @ts-expect-error
assoc('what')('foo')(obj)

const result3 = assoc('what')(2)({} as Record)
result3.what // $ExpectType number

const result4 = assoc('str', 'bar')(obj)
result4.str // $ExpectType string
result4.num // $ExpectType number

assoc('str', 2)(obj)
assoc('what', 'bar')(obj)

const result5 = assoc('str', 2)({} as Record)
result5.str // $ExpectType number

const result6 = assoc('str', 'bar', obj)
result6.str // $ExpectType string
result6.num // $ExpectType number

// @ts-expect-error
assoc('str', 2, obj)
// @ts-expect-error

assoc('what', 'bar', obj)

const result7 = assoc('str', 2, {} as Record)
result7.str // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assoc)

### assocPath

```typescript

assocPath(path: Path, newValue: any, obj: object): Output
```

It makes a shallow clone of `obj` with setting or overriding with `newValue` the property found with `path`.

```javascript
const path = 'b.c'
const newValue = 2
const obj = { a: 1 }

const result = R.assocPath(path, newValue, obj)
// => { a : 1, b : { c : 2 }}
```

All TypeScript definitions

```typescript
assocPath(path: Path, newValue: any, obj: object): Output;
assocPath(path: Path, newValue: any): (obj: object) => Output;
assocPath(path: Path): (newValue: any) => (obj: object) => Output;
```

R.assocPath source

```javascript
import { cloneList } from './_internals/cloneList.js'
import { createPath } from './_internals/createPath.js'
import { isArray } from './_internals/isArray.js'
import { isIndexInteger } from './_internals/isInteger.js'
import { assocFn } from './assoc.js'
import { curry } from './curry.js'

export function assocPathFn(
path, newValue, input
){
const pathArrValue = createPath(path)
if (pathArrValue.length === 0) return newValue

const index = pathArrValue[ 0 ]
if (pathArrValue.length > 1){
const condition =
typeof input !== 'object' ||
input === null ||
!input.hasOwnProperty(index)

const nextInput = condition ?
isIndexInteger(pathArrValue[ 1 ]) ?
[] :
{} :
input[ index ]

newValue = assocPathFn(
Array.prototype.slice.call(pathArrValue, 1),
newValue,
nextInput
)
}

if (isIndexInteger(index) && isArray(input)){
const arr = cloneList(input)
arr[ index ] = newValue

return arr
}

return assocFn(
index, newValue, input
)
}

export const assocPath = curry(assocPathFn)
```

Tests

```javascript
import { assocPathFn } from './assocPath.js'

test('happy', () => {
const path = 'a.c.1'
const input = {
a : {
b : 1,
c : [ 1, 2 ],
},
}
assocPathFn(
path, 3, input
)
expect(input).toEqual({
a : {
b : 1,
c : [ 1, 2 ],
},
})
})

test('string can be used as path input', () => {
const testObj = {
a : [ { b : 1 }, { b : 2 } ],
d : 3,
}
const result1 = assocPathFn(
[ 'a', 0, 'b' ], 10, testObj
)
const result2 = assocPathFn(
'a.0.b', 10, testObj
)

const expected = {
a : [ { b : 10 }, { b : 2 } ],
d : 3,
}
expect(result1).toEqual(expected)
expect(result2).toEqual(expected)
})

test('difference with ramda - doesn\'t overwrite primitive values with keys in the path', () => {
const obj = { a : 'str' }
const result = assocPathFn(
[ 'a', 'b' ], 42, obj
)

expect(result).toEqual({
a : {
0 : 's',
1 : 't',
2 : 'r',
b : 42,
},
})
})

test('adds a key to an empty object', () => {
expect(assocPathFn(
[ 'a' ], 1, {}
)).toEqual({ a : 1 })
})

test('adds a key to a non-empty object', () => {
expect(assocPathFn(
'b', 2, { a : 1 }
)).toEqual({
a : 1,
b : 2,
})
})

test('adds a nested key to a non-empty object', () => {
expect(assocPathFn(
'b.c', 2, { a : 1 }
)).toEqual({
a : 1,
b : { c : 2 },
})
})

test('adds a nested key to a nested non-empty object', () => {
expect(assocPathFn('b.d',
3,{
a : 1,
b : { c : 2 },
})).toEqual({
a : 1,
b : {
c : 2,
d : 3,
},
})
})

test('adds a key to a non-empty object', () => {
expect(assocPathFn('b', 2, { a : 1 })).toEqual({
a : 1,
b : 2,
})
})

test('adds a nested key to a non-empty object', () => {
expect(assocPathFn('b.c', 2, { a : 1 })).toEqual({
a : 1,
b : { c : 2 },
})
})

test('changes an existing key', () => {
expect(assocPathFn(
'a', 2, { a : 1 }
)).toEqual({ a : 2 })
})

test('undefined is considered an empty object', () => {
expect(assocPathFn(
'a', 1, undefined
)).toEqual({ a : 1 })
})

test('null is considered an empty object', () => {
expect(assocPathFn(
'a', 1, null
)).toEqual({ a : 1 })
})

test('value can be null', () => {
expect(assocPathFn(
'a', null, null
)).toEqual({ a : null })
})

test('value can be undefined', () => {
expect(assocPathFn(
'a', undefined, null
)).toEqual({ a : undefined })
})

test('assignment is shallow', () => {
expect(assocPathFn(
'a', { b : 2 }, { a : { c : 3 } }
)).toEqual({ a : { b : 2 } })
})

test('empty array as path', () => {
const result = assocPathFn(
[], 3, {
a : 1,
b : 2,
}
)
expect(result).toBe(3)
})

test('happy', () => {
const expected = { foo : { bar : { baz : 42 } } }
const result = assocPathFn(
[ 'foo', 'bar', 'baz' ], 42, { foo : null }
)
expect(result).toEqual(expected)
})
```

TypeScript test

```typescript
import {assocPath} from 'rambda'

interface Output {
a: number,
foo: {bar: number},
}

describe('R.assocPath - user must explicitly set type of output', () => {
it('with array as path input', () => {
const result = assocPath(['foo', 'bar'], 2, {a: 1})

result // $ExpectType Output
})
it('with string as path input', () => {
const result = assocPath('foo.bar', 2, {a: 1})

result // $ExpectType Output
})
})

describe('R.assocPath - curried', () => {
it('with array as path input', () => {
const result = assocPath(['foo', 'bar'], 2)({a: 1})

result // $ExpectType Output
})
it('with string as path input', () => {
const result = assocPath('foo.bar', 2)({a: 1})

result // $ExpectType Output
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#assocPath)

### binary

```typescript

binary any>(fn: T): (...args: any[]) => ReturnType
```

```javascript
const result = R.binary(
(a, b, c) => a + b + c,
)(1, 2, 3, 4)
// => 3
```

All TypeScript definitions

```typescript
binary any>(fn: T): (...args: any[]) => ReturnType;
```

R.binary source

```javascript
export function binary(fn){
if (fn.length <= 2) return fn

return (a, b) => fn(a, b)
}
```

Tests

```javascript
import { binary } from './binary.js'

test('happy', () => {
const result = binary(function (
x, y, z
){
expect(arguments).toHaveLength(2)
expect(z).toBeUndefined()
expect(x).toBe(10)
expect(y).toBe(20)

return x + y
})(
10, 20, 30
)
expect(result).toBe(30)
})
```

TypeScript test

```typescript
import {binary} from 'rambda'

describe('R.binary', () => {
it('happy', () => {
const result = binary(function(x: number, y: number, z) {
expect(arguments.length).toBe(2)
expect(z).toBeUndefined()
expect(x).toBe(10)
expect(y).toBe(20)
return x + y
})(10, 20, 30)
result // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#binary)

### bind

```typescript

bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType
```

Creates a function that is bound to a context.

> :boom: R.bind does not provide the additional argument-binding capabilities of `Function.prototype.bind`.

```javascript
const log = R.bind(console.log, console)
const result = R.pipe(
R.assoc('a', 2),
R.tap(log),
R.assoc('a', 3)
)({a: 1});
// => result - `{a: 3}`
// => console log - `{a: 2}`
```

All TypeScript definitions

```typescript
bind(fn: F, thisObj: T): (...args: Parameters) => ReturnType;
bind(fn: F): (thisObj: T) => (...args: Parameters) => ReturnType;
```

R.bind source

```javascript
import { curryN } from './curryN.js'

export function bind(fn, thisObj){
if (arguments.length === 1){
return _thisObj => bind(fn, _thisObj)
}

return curryN(fn.length, (...args) => fn.apply(thisObj, args))
}
```

Tests

```javascript
import { bind } from './bind.js'

function Foo(x){
this.x = x
}
function add(x){
return this.x + x
}
function Bar(x, y){
this.x = x
this.y = y
}
Bar.prototype = new Foo()
Bar.prototype.getX = function (){
return 'prototype getX'
}

test('returns a function', () => {
expect(typeof bind(add)(Foo)).toBe('function')
})

test('returns a function bound to the specified context object', () => {
const f = new Foo(12)
function isFoo(){
return this instanceof Foo
}
const isFooBound = bind(isFoo, f)
expect(isFoo()).toBeFalse()
expect(isFooBound()).toBeTrue()
})

test('works with built-in types', () => {
const abc = bind(String.prototype.toLowerCase, 'ABCDEFG')
expect(typeof abc).toBe('function')
expect(abc()).toBe('abcdefg')
})

test('works with user-defined types', () => {
const f = new Foo(12)
function getX(){
return this.x
}
const getXFooBound = bind(getX, f)
expect(getXFooBound()).toBe(12)
})

test('works with plain objects', () => {
const pojso = { x : 100 }
function incThis(){
return this.x + 1
}
const incPojso = bind(incThis, pojso)
expect(typeof incPojso).toBe('function')
expect(incPojso()).toBe(101)
})

test('does not interfere with existing object methods', () => {
const b = new Bar('a', 'b')
function getX(){
return this.x
}
const getXBarBound = bind(getX, b)
expect(b.getX()).toBe('prototype getX')
expect(getXBarBound()).toBe('a')
})

test('preserves arity', () => {
const f0 = function (){
return 0
}
const f1 = function (a){
return a
}
const f2 = function (a, b){
return a + b
}
const f3 = function (
a, b, c
){
return a + b + c
}

expect(bind(f0, {})).toHaveLength(0)
expect(bind(f1, {})).toHaveLength(1)
expect(bind(f2, {})).toHaveLength(2)
expect(bind(f3, {})).toHaveLength(3)
})
```

TypeScript test

```typescript
import {bind} from 'rambda'

class Foo {}
function isFoo(this: T): boolean {
return this instanceof Foo
}

describe('R.bind', () => {
it('happy', () => {
const foo = new Foo()
const result = bind(isFoo, foo)()

result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#bind)

### both

```typescript

both(pred1: Pred, pred2: Pred): Pred
```

It returns a function with `input` argument.

This function will return `true`, if both `firstCondition` and `secondCondition` return `true` when `input` is passed as their argument.

```javascript
const firstCondition = x => x > 10
const secondCondition = x => x < 20
const fn = R.both(firstCondition, secondCondition)

const result = [fn(15), fn(30)]
// => [true, false]
```

All TypeScript definitions

```typescript
both(pred1: Pred, pred2: Pred): Pred;
both(pred1: Predicate, pred2: Predicate): Predicate;
both(pred1: Predicate): (pred2: Predicate) => Predicate;
both(pred1: Pred): (pred2: Pred) => Pred;
```

R.both source

```javascript
export function both(f, g){
if (arguments.length === 1) return _g => both(f, _g)

return (...input) => f(...input) && g(...input)
}
```

Tests

```javascript
import { both } from './both.js'

const firstFn = val => val > 0
const secondFn = val => val < 10

test('with curry', () => {
expect(both(firstFn)(secondFn)(17)).toBeFalse()
})

test('without curry', () => {
expect(both(firstFn, secondFn)(7)).toBeTrue()
})

test('with multiple inputs', () => {
const between = function (
a, b, c
){
return a < b && b < c
}
const total20 = function (
a, b, c
){
return a + b + c === 20
}
const fn = both(between, total20)
expect(fn(
5, 7, 8
)).toBeTrue()
})

test('skip evaluation of the second expression', () => {
let effect = 'not evaluated'
const F = function (){
return false
}
const Z = function (){
effect = 'Z got evaluated'
}
both(F, Z)()

expect(effect).toBe('not evaluated')
})
```

TypeScript test

```typescript
import {both} from 'rambda'

describe('R.both', () => {
it('with passed type', () => {
const fn = both(
x => x > 1,
x => x % 2 === 0
)
fn // $ExpectType Predicate
const result = fn(2) // $ExpectType boolean
result // $ExpectType boolean
})
it('with passed type - curried', () => {
const fn = both(x => x > 1)(x => x % 2 === 0)
fn // $ExpectType Predicate
const result = fn(2)
result // $ExpectType boolean
})
it('no type passed', () => {
const fn = both(
x => {
x // $ExpectType any
return x > 1
},
x => {
x // $ExpectType any
return x % 2 === 0
}
)
const result = fn(2)
result // $ExpectType boolean
})
it('no type passed - curried', () => {
const fn = both((x: number) => {
x // $ExpectType number
return x > 1
})((x: number) => {
x // $ExpectType number
return x % 2 === 0
})
const result = fn(2)
result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#both)

### call

```typescript

call any>(fn: T, ...args: Parameters): ReturnType
```

```javascript
const result = R.call(
(a, b) => a + b,
1,
2
)
// => 3
```

All TypeScript definitions

```typescript
call any>(fn: T, ...args: Parameters): ReturnType;
```

R.call source

```javascript
export const call = (fn, ...inputs) => fn(...inputs)
```

Tests

```javascript
import { bind } from './bind.js'
import { call } from './call.js'

test('happy', () => {
expect(call(
Math.max, 1, 2, 3, -99, 42, 6, 7
)).toBe(42)
})

test('accepts one or more arguments', () => {
const fn = function (){
return arguments.length
}
expect(call(fn)).toBe(0)
expect(call(fn, 'x')).toBe(1)
expect(call(
fn, 'x', 'y'
)).toBe(2)
expect(call(
fn, 'x', 'y', 'z'
)).toBe(3)
})

test('provides no way to specify context', () => {
var obj = {
method (){
return this === obj
},
}
expect(call(obj.method)).toBe(false)
expect(call(bind(obj.method, obj))).toBe(true)
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#call)

### chain

```typescript

chain(fn: (n: T) => U[], list: T[]): U[]
```

The method is also known as `flatMap`.

```javascript
const duplicate = n => [ n, n ]
const list = [ 1, 2, 3 ]

const result = chain(duplicate, list)
// => [ 1, 1, 2, 2, 3, 3 ]
```

All TypeScript definitions

```typescript
chain(fn: (n: T) => U[], list: T[]): U[];
chain(fn: (n: T) => U[]): (list: T[]) => U[];
```

R.chain source

```javascript
export function chain(fn, list){
if (arguments.length === 1){
return _list => chain(fn, _list)
}

return [].concat(...list.map(fn))
}
```

Tests

```javascript
import { chain as chainRamda } from 'ramda'

import { chain } from './chain.js'

const duplicate = n => [ n, n ]

test('happy', () => {
const fn = x => [ x * 2 ]
const list = [ 1, 2, 3 ]

const result = chain(fn, list)

expect(result).toEqual([ 2, 4, 6 ])
})

test('maps then flattens one level', () => {
expect(chain(duplicate, [ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ])
})

test('maps then flattens one level - curry', () => {
expect(chain(duplicate)([ 1, 2, 3 ])).toEqual([ 1, 1, 2, 2, 3, 3 ])
})

test('flattens only one level', () => {
const nest = n => [ [ n ] ]
expect(chain(nest, [ 1, 2, 3 ])).toEqual([ [ 1 ], [ 2 ], [ 3 ] ])
})

test('can compose', () => {
function dec(x){
return [ x - 1 ]
}
function times2(x){
return [ x * 2 ]
}

const mdouble = chain(times2)
const mdec = chain(dec)
expect(mdec(mdouble([ 10, 20, 30 ]))).toEqual([ 19, 39, 59 ])
})

test('@types/ramda broken test', () => {
const score = {
maths : 90,
physics : 80,
}

const calculateTotal = score => {
const { maths, physics } = score

return maths + physics
}

const assocTotalToScore = (total, score) => ({
...score,
total,
})

const calculateAndAssocTotalToScore = chainRamda(assocTotalToScore,
calculateTotal)
expect(() =>
calculateAndAssocTotalToScore(score)).toThrowErrorMatchingInlineSnapshot('"fn(...) is not a function"')
})
```

TypeScript test

```typescript
import {chain} from 'rambda'

const list = [1, 2, 3]
const fn = (x: number) => [`${x}`, `${x}`]

describe('R.chain', () => {
it('without passing type', () => {
const result = chain(fn, list)
result // $ExpectType string[]

const curriedResult = chain(fn)(list)
curriedResult // $ExpectType string[]
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#chain)

### clamp

```typescript

clamp(min: number, max: number, input: number): number
```

Restrict a number `input` to be within `min` and `max` limits.

If `input` is bigger than `max`, then the result is `max`.

If `input` is smaller than `min`, then the result is `min`.

```javascript
const result = [
R.clamp(0, 10, 5),
R.clamp(0, 10, -1),
R.clamp(0, 10, 11)
]
// => [5, 0, 10]
```

All TypeScript definitions

```typescript
clamp(min: number, max: number, input: number): number;
clamp(min: number, max: number): (input: number) => number;
```

R.clamp source

```javascript
import { curry } from './curry.js'

function clampFn(
min, max, input
){
if (min > max){
throw new Error('min must not be greater than max in clamp(min, max, value)')
}
if (input >= min && input <= max) return input

if (input > max) return max
if (input < min) return min
}

export const clamp = curry(clampFn)
```

Tests

```javascript
import { clamp } from './clamp.js'

test('when min is greater than max', () => {
expect(() => clamp(
-5, -10, 5
)).toThrowErrorMatchingInlineSnapshot('"min must not be greater than max in clamp(min, max, value)"')
})

test('rambda specs', () => {
expect(clamp(
1, 10, 0
)).toBe(1)
expect(clamp(
3, 12, 1
)).toBe(3)
expect(clamp(
-15, 3, -100
)).toBe(-15)
expect(clamp(
1, 10, 20
)).toBe(10)
expect(clamp(
3, 12, 23
)).toBe(12)
expect(clamp(
-15, 3, 16
)).toBe(3)
expect(clamp(
1, 10, 4
)).toBe(4)
expect(clamp(
3, 12, 6
)).toBe(6)
expect(clamp(
-15, 3, 0
)).toBe(0)
})
```

TypeScript test

```typescript
import {clamp} from 'rambda'

describe('R.clamp', () => {
it('happy', () => {
const result = clamp(1, 10, 20)
result // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clamp)

### clone

```typescript

clone(input: T): T
```

It creates a deep copy of the `input`, which may contain (nested) Arrays and Objects, Numbers, Strings, Booleans and Dates.

> :boom: It doesn't work with very specific types, such as MongoDB's ObjectId.

```javascript
const objects = [{a: 1}, {b: 2}];
const objectsClone = R.clone(objects);

const result = [
R.equals(objects, objectsClone),
R.equals(objects[0], objectsClone[0]),
] // => [ true, true ]
```

All TypeScript definitions

```typescript
clone(input: T): T;
clone(input: T[]): T[];
```

R.clone source

```javascript
import { isArray } from './_internals/isArray.js'

export function clone(input){
const out = isArray(input) ? Array(input.length) : {}
if (input && input.getTime) return new Date(input.getTime())

for (const key in input){
const v = input[ key ]
out[ key ] =
typeof v === 'object' && v !== null ?
v.getTime ?
new Date(v.getTime()) :
clone(v) :
v
}

return out
}
```

Tests

```javascript
import assert from 'assert'
import { clone as cloneRamda } from 'ramda'

import {
compareCombinations,
EXTRA_BUILD_IN_OBJECTS,
FALSY_VALUES,
} from './_internals/testUtils.js'
import { clone } from './clone.js'
import { equals } from './equals.js'

test('with array', () => {
const arr = [
{
b : 2,
c : 'foo',
d : [ 1, 2, 3 ],
},
1,
new Date(),
null,
]
expect(clone(arr)).toEqual(arr)
})

test('with object', () => {
const obj = {
a : 1,
b : 2,
c : 3,
d : [ 1, 2, 3 ],
e : new Date(),
}
expect(clone(obj)).toEqual(obj)
})

test('with date', () => {
const date = new Date(
2014, 10, 14, 23, 59, 59, 999
)

const cloned = clone(date)
assert.notStrictEqual(date, cloned)
expect(cloned).toEqual(new Date(
2014, 10, 14, 23, 59, 59, 999
))

expect(cloned.getDay()).toBe(5)
})

test('with R.equals', () => {
const objects = [ { a : 1 }, { b : 2 } ]

const objectsClone = clone(objects)

const result = [
equals(objects, objectsClone),
equals(objects[ 0 ], objectsClone[ 0 ]),
]
expect(result).toEqual([ true, true ])
})

describe('brute force', () => {
const possibleInputs = [ ...FALSY_VALUES, ...EXTRA_BUILD_IN_OBJECTS ]
compareCombinations({
fn : clone,
fnRamda : cloneRamda,
firstInput : possibleInputs,
callback : errorsCounters => {
expect(errorsCounters).toMatchInlineSnapshot(`
{
"ERRORS_MESSAGE_MISMATCH": 0,
"ERRORS_TYPE_MISMATCH": 0,
"RESULTS_MISMATCH": 15,
"SHOULD_NOT_THROW": 0,
"SHOULD_THROW": 0,
"TOTAL_TESTS": 15,
}
`)
},
})
})
```

TypeScript test

```typescript
import {clone} from 'rambda'

describe('R.clone', () => {
it('happy', () => {
const obj = {a: 1, b: 2}
const result = clone(obj)
result // $ExpectType { a: number; b: number; }
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#clone)

### collectBy

```typescript

collectBy(keyFn: (value: T) => K, list: T[]): T[][]
```

```javascript
const result = R.collectBy(
x => x % 2,
[1, 2, 3, 4]
)
// => [[2, 4], [1, 3]]
```

All TypeScript definitions

```typescript
collectBy(keyFn: (value: T) => K, list: T[]): T[][];
collectBy(keyFn: (value: T) => K): (list: T[]) => T[][];
```

R.collectBy source

```javascript
import { reduce } from './reduce.js'

export function collectBy(fn, list){
if (arguments.length === 1){
return _list => collectBy(fn, _list)
}

const group = reduce(
(o, x) => {
const tag = fn(x)
if (o[ tag ] === undefined){
o[ tag ] = []
}
o[ tag ].push(x)

return o
},
{},
list
)
const newList = []
for (const tag in group){
newList.push(group[ tag ])
}

return newList
}
```

Tests

```javascript
import fc from 'fast-check'
import {
all,
compose,
difference,
equals,
head,
identity,
is,
isEmpty,
length,
uniq,
unnest,
} from 'rambdax'

import { collectBy } from './collectBy.js'

test('returns a list of lists', () => {
fc.assert(fc.property(fc.array(fc.nat()), xs => {
const check = all(is(Array))
const ys = collectBy(identity)(xs)

return check(ys)
}))
})

test('groups items but neither adds new ones nor removes any', () => {
fc.assert(fc.property(fc.array(fc.nat()), xs => {
const check = compose(
isEmpty, difference(xs), unnest
)
const ys = collectBy(identity)(xs)

return check(ys)
}))
})

test('groups related items together', () => {
fc.assert(fc.property(fc.array(fc.boolean()), xs => {
const ys = collectBy(identity)(xs)
const check = all(compose(
equals(1), length, uniq
))

return check(ys)
}))
})

test('invokes the tag function for each item in the list', () => {
fc.assert(fc.property(fc.array(fc.nat()), xs => {
const id = jest.fn(x => 42)
collectBy(id)(xs)
const check = compose(isEmpty, difference(xs))

return check(id.mock.calls.map(call => call[ 0 ]))
}))
})

test('groups items according to the tag value', () => {
fc.assert(fc.property(fc.array(fc.nat()), xs => {
const ys = collectBy(x => 42)(xs)
const check = compose(
isEmpty, difference(xs), head
)

return isEmpty(xs) && isEmpty(ys) ? true : check(ys)
}))
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#collectBy)

### comparator

```typescript

comparator(pred: (a: T, b: T) => boolean): (x: T, y: T) => Ordering
```

It returns a comparator function that can be used in `sort` method.

```javascript
const result = R.sort(
R.comparator((a, b) => a.x < b.x),
[{x: 2}, {x: 1}]
)
// => [{x: 1}, {x: 2}]
```

All TypeScript definitions

```typescript
comparator(pred: (a: T, b: T) => boolean): (x: T, y: T) => Ordering;
```

R.comparator source

```javascript
export function comparator(fn){
return function (a, b){
return fn(a, b) ? -1 : fn(b, a) ? 1 : 0
}
}
```

Tests

```javascript
import { comparator } from './comparator.js'

test('happy', () => {
expect([ 3, 1, 8, 1, 2, 5 ].sort(comparator((a, b) => a < b))).toEqual([
1, 1, 2, 3, 5, 8,
])
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#comparator)

### complement

```typescript

complement(predicate: (...args: T) => unknown): (...args: T) => boolean
```

It returns `inverted` version of `origin` function that accept `input` as argument.

The return value of `inverted` is the negative boolean value of `origin(input)`.

```javascript
const origin = x => x > 5
const inverted = complement(origin)

const result = [
origin(7),
inverted(7)
] => [ true, false ]
```

All TypeScript definitions

```typescript
complement(predicate: (...args: T) => unknown): (...args: T) => boolean;
```

R.complement source

```javascript
export function complement(fn){
return (...input) => !fn(...input)
}
```

Tests

```javascript
import { complement } from './complement.js'

test('happy', () => {
const fn = complement(x => x.length === 0)

expect(fn([ 1, 2, 3 ])).toBeTrue()
})

test('with multiple parameters', () => {
const between = function (
a, b, c
){
return a < b && b < c
}
const f = complement(between)
expect(f(
4, 5, 11
)).toBeFalse()
expect(f(
12, 2, 6
)).toBeTrue()
})
```

TypeScript test

```typescript
import {complement, isNil} from 'rambda'

describe('R.complement', () => {
it('happy', () => {
const fn = complement(isNil)
const result = fn(null)
result // $ExpectType boolean
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#complement)

### compose

```typescript

compose(
...func: [
fnLast: (a: any) => TResult,
...func: Array<(a: any) => any>,
f7: (a: R6) => R7,
f6: (a: R5) => R6,
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
]
): (...args: TArgs) => TResult
```

It performs right-to-left function composition.

```javascript
const result = R.compose(
R.map(x => x * 2),
R.filter(x => x > 2)
)([1, 2, 3, 4])

// => [6, 8]
```

All TypeScript definitions

```typescript
compose(
...func: [
fnLast: (a: any) => TResult,
...func: Array<(a: any) => any>,
f7: (a: R6) => R7,
f6: (a: R5) => R6,
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
]
): (...args: TArgs) => TResult;
compose(
f7: (a: R6) => R7,
f6: (a: R5) => R6,
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R7;
compose(
f7: (a: R6) => R7,
f6: (a: R5) => R6,
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R7;
compose(
f6: (a: R5) => R6,
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R6;
compose(
f5: (a: R4) => R5,
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R5;
compose(
f4: (a: R3) => R4,
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R4;
compose(
f3: (a: R2) => R3,
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R3;
compose(
f2: (a: R1) => R2,
f1: (...args: TArgs) => R1
): (...args: TArgs) => R2;
compose(
f1: (...args: TArgs) => R1
): (...args: TArgs) => R1;
```

R.compose source

```javascript
import { pipe } from './pipe.js'

export function compose(){
if (arguments.length === 0){
throw new Error('compose requires at least one argument')
}

return pipe.apply(this, Array.prototype.slice.call(arguments, 0).reverse())
}
```

Tests

```javascript
import { compose as composeRamda } from 'ramda'

import { add } from './add.js'
import { compose } from './compose.js'
import { filter } from './filter.js'
import { last } from './last.js'
import { map } from './map.js'

test('happy', () => {
const result = compose(
last, map(add(10)), map(add(1))
)([ 1, 2, 3 ])

expect(result).toBe(14)
})

test('can accepts initially two arguments', () => {
const result = compose(map(x => x * 2),
(list, limit) => filter(x => x > limit, list))([ 1, 2, 3, 4, false ], 2)

expect(result).toEqual([ 6, 8 ])
})

test('when no arguments is passed', () => {
expect(() => compose()).toThrowErrorMatchingInlineSnapshot('"compose requires at least one argument"')
})

test('ramda spec', () => {
const f = function (
a, b, c
){
return [ a, b, c ]
}
const g = compose(f)

expect(g(
1, 2, 3
)).toEqual([ 1, 2, 3 ])
})

test('does return correct length of composed function', () => {
expect(compose(
map, map, map
)).toHaveLength(2)
expect(composeRamda(
map, map, map
)).toHaveLength(2)
})
```

TypeScript test

```typescript
import {
add,
subtract,
compose,
map,
filter,
identity,
inc,
negate,
dissoc,
} from 'rambda'

interface Input {
a: string,
b: string,
}
interface Output {
c: string,
}

describe('R.compose with explicit types', () => {
it('with explicit types - complex', () => {
const obj = {
a: 'foo',
b: 'bar',
}
interface AfterInput {
a: number,
}
interface BeforeOutput {
b: string,
}

const result = compose(
x => ({c: x.b + 'bar'}),
x => ({b: x.a + 'foo'}),
x => ({a: x.a.length + x.b.length})
)(obj)

result // $ExpectType Output
})
it('with explicit types - correct', () => {
const obj = {
a: 'foo',
b: 'bar',
}
const result = compose(identity, input => {
input // $ExpectType Input
return input as unknown as Output
})(obj)
result // $ExpectType Output
})
it('with explicit types - wrong', () => {
const obj: Input = {
a: 'foo',
b: 'bar',
}

// @ts-expect-error
compose(identity, dissoc('b'))(obj)
})
})

describe('R.compose', () => {
it('happy', () => {
const result = compose(subtract(11), add(1), add(1))(1)
result // $ExpectType number
})
it('happy - more complex', () => {
const result = compose(
(x: number) => x + 1,
(x: string) => x.length + 1
)('foo')
result // $ExpectType number
})

it('with R.filter', () => {
const result = compose(
filter(x => x > 2),
map(add(1))
)([1, 2, 3])
result // $ExpectType number[]
})

it('with native filter', () => {
const result = compose(
(list: number[]) => list.filter(x => x > 2),
(list: number[]) => {
list // $ExpectType number[]
return list
},
map(add(1))
)([1, 2, 3])

result // $ExpectType number[]
})

it('with void', () => {
const result = compose(
() => {},
() => {}
)()
result // $ExpectType void
})
})

describe('R.compose - @types/ramda tests', () => {
test('complex', () => {
const fn = compose(
inc,
inc,
inc,
inc,
inc,
inc,
inc,
inc,
negate,
Math.pow
)
const result = fn(3, 4)
result // $ExpectType number
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#compose)

### composeWith

```typescript

composeWith(
transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any,
fns: AtLeastOneFunctionsFlowFromRightToLeft,
): (...args: TArgs) => TResult
```

```javascript
const result = R.composeWith(
(fn, intermediateResult) => fn(intermediateResult),
[
R.map(x => x + 1),
R.map(x => x * 2),
]
)([1, 2, 3])
// => [3, 5, 7]
```

All TypeScript definitions

```typescript
composeWith(
transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any,
fns: AtLeastOneFunctionsFlowFromRightToLeft,
): (...args: TArgs) => TResult;
composeWith(
transformer: (fn: (...args: any[]) => any, intermediatResult: any) => any,
): (
fns: AtLeastOneFunctionsFlowFromRightToLeft,
) => (...args: TArgs) => TResult;
```

R.composeWith source

```javascript
import { _arity } from './_internals/_arity.js'
import { head } from './head.js'
import { identity } from './identity.js'
import { reduce } from './reduce.js'
import { reverse } from './reverse.js'
import { tail } from './tail.js'

export function pipeWith(xf, list){
if (list.length <= 0){
return identity
}

const headList = head(list)
const tailList = tail(list)

return _arity(headList.length, function (){
return reduce(
function (result, f){
return xf.call(
this, f, result
)
},
headList.apply(this, arguments),
tailList
)
})
}

export function composeWith(xf, list){
if (arguments.length === 1) return _list => composeWith(xf, _list)

return pipeWith.apply(this, [ xf, reverse(list) ])
}
```

Tests

```javascript
import { always, identity, inc, isNil, map, modulo, multiply } from 'rambdax'
import { composeWith as composeWithRamda, concat, flip, ifElse } from 'ramda'

import { composeWith } from './composeWith.js'

test('performs right-to-left function composition with function applying', () => {
const f = composeWith((f, res) => f(res))([ map, multiply, parseInt ])

expect(f).toHaveLength(2)
expect(f('10')([ 1, 2, 3 ])).toEqual([ 10, 20, 30 ])
expect(f('10', 2)([ 1, 2, 3 ])).toEqual([ 2, 4, 6 ])
})

test('performs right-to-left function while not nil result', () => {
const isOdd = flip(modulo)(2)
const composeWhenNotNil = composeWithRamda((f, res) =>
isNil(res) ? null : f(res))

const f = composeWhenNotNil([
inc,
ifElse(
isOdd, identity, always(null)
),
parseInt,
])
expect(f).toHaveLength(2)
expect(f('1')).toBe(2)
expect(f('2')).toBeNull()
})

test('performs right-to-left function using promise chaining', () => {
const then = function (f, p){
return p.then(f)
}
const composeP = composeWithRamda(then)
const toListPromise = function (a){
return new Promise(res => {
res([ a ])
})
}
const doubleListPromise = function (a){
return new Promise(res => {
res(concat(a, a))
})
}
const f = composeP([ doubleListPromise, toListPromise ])

return f(1).then(res => {
expect(res).toEqual([ 1, 1 ])
})
})
```

[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/files/separator.png)](#composeWith)

### concat

```typescript

concat(x: T