https://github.com/selfrefactor/rambdax
Extended version of Rambda
https://github.com/selfrefactor/rambdax
functional-programming lodash rambda ramda utils
Last synced: about 1 year ago
JSON representation
Extended version of Rambda
- Host: GitHub
- URL: https://github.com/selfrefactor/rambdax
- Owner: selfrefactor
- License: mit
- Created: 2017-04-07T16:54:26.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2025-04-01T14:31:29.000Z (about 1 year ago)
- Last Synced: 2025-04-07T01:07:59.080Z (about 1 year ago)
- Topics: functional-programming, lodash, rambda, ramda, utils
- Language: JavaScript
- Homepage: https://selfrefactor.github.io/rambdax
- Size: 5.17 MB
- Stars: 221
- Watchers: 5
- Forks: 26
- Open Issues: 0
-
Metadata Files:
- Readme: .github/README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-fp-js - Rambdax - Extended version of Rambda (Libraries)
- popular-dependents-lists - selfrefactor/rambdax - 🌟 189 (Rambdax / selfrefactor/rambdax)
- awesome-fp-js - Rambdax - Extended version of Rambda (Libraries)
README
# Rambda
`Rambda` is smaller and faster alternative to the popular functional programming library **Ramda**. - [Documentation](https://selfrefactor.github.io/rambda/#/)
[](https://circleci.com/gh/selfrefactor/rambda/tree/master)
[](https://codecov.io/gh/selfrefactor/rambda)



[](https://packagephobia.com/result?p=rambda)
[](https://nest.land/package/rambda)
[](https://github.com/selfrefactor/rambda/pulls)
## ❯ 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)
[](#-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} })
```
### 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.
[](#-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):
[](#-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";
```
[](#-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)
[](#-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 | 🔳
[](#-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)
[](#-used-by)
## API
### add
It adds `a` and `b`.
> :boom: It doesn't work with strings, as the inputs are parsed to numbers before calculation.
Try this R.add example in Rambda REPL
[](#add)
### addIndex
Try this R.addIndex example in Rambda REPL
[](#addIndex)
### addIndexRight
Same as `R.addIndex`, but it will passed indexes are decreasing, instead of increasing.
[](#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]
```
Try this R.adjust example in Rambda REPL
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)
})
```
[](#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
```
Try this R.all example in Rambda REPL
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
})
})
```
[](#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
```
Try this R.allPass example in Rambda REPL
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
})
})
```
[](#allPass)
### always
It returns function that always returns `x`.
Try this R.always example in Rambda REPL
[](#always)
### and
Logical AND
Try this R.and example in Rambda REPL
[](#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
```
Try this R.any example in Rambda REPL
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
})
})
```
[](#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
```
Try this R.anyPass example in Rambda REPL
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
}
})
})
```
[](#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]
```
Try this R.ap example in Rambda REPL
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 ])
})
```
[](#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]]
```
Try this R.aperture example in Rambda REPL
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([])
})
```
[](#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']
```
Try this R.append example in Rambda REPL
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)[]
})
})
})
```
[](#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
```
Try this R.apply example in Rambda REPL
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
})
})
```
[](#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 } }
```
Try this R.applySpec example in Rambda REPL
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
})
})
```
[](#applySpec)
### applyTo
Try this R.applyTo example in Rambda REPL
[](#applyTo)
### ascend
Try this R.ascend example in Rambda REPL
[](#ascend)
### assoc
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.
Try this R.assoc example in Rambda REPL
[](#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 }
R.assocPath(path, newValue, Record)
// => { a : 1, b : { c : 2 }}
```
Try this R.assocPath example in Rambda REPL
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.only('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 = assocPath(
[ 'a', 'b' ], 42, obj
)
expect(result).toEqual({
a : {
0 : 's',
1 : 't',
2 : 'r',
b : 42,
},
})
})
test('bug', () => {
/*
https://github.com/selfrefactor/rambda/issues/524
*/
const state = {}
const withDateLike = assocPath(
[ 'outerProp', '2020-03-10' ],
{ prop : 2 },
state
)
const withNumber = assocPath(
[ 'outerProp', '5' ], { prop : 2 }, state
)
const withDateLikeExpected = { outerProp : { '2020-03-10' : { prop : 2 } } }
const withNumberExpected = { outerProp : { 5 : { prop : 2 } } }
expect(withDateLike).toEqual(withDateLikeExpected)
expect(withNumber).toEqual(withNumberExpected)
})
test('adds a key to an empty object', () => {
expect(assocPath(
[ 'a' ], 1, {}
)).toEqual({ a : 1 })
})
test('adds a key to a non-empty object', () => {
expect(assocPath(
'b', 2, { a : 1 }
)).toEqual({
a : 1,
b : 2,
})
})
test('adds a nested key to a non-empty object', () => {
expect(assocPath(
'b.c', 2, { a : 1 }
)).toEqual({
a : 1,
b : { c : 2 },
})
})
test('adds a nested key to a nested non-empty object - curry case 1', () => {
expect(assocPath('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 - curry case 1', () => {
expect(assocPath('b', 2)({ a : 1 })).toEqual({
a : 1,
b : 2,
})
})
test('adds a nested key to a non-empty object - curry case 1', () => {
expect(assocPath('b.c', 2)({ a : 1 })).toEqual({
a : 1,
b : { c : 2 },
})
})
test('adds a key to a non-empty object - curry case 2', () => {
expect(assocPath('b')(2, { a : 1 })).toEqual({
a : 1,
b : 2,
})
})
test('adds a key to a non-empty object - curry case 3', () => {
const result = assocPath('b')(2)({ a : 1 })
expect(result).toEqual({
a : 1,
b : 2,
})
})
test('changes an existing key', () => {
expect(assocPath(
'a', 2, { a : 1 }
)).toEqual({ a : 2 })
})
test('undefined is considered an empty object', () => {
expect(assocPath(
'a', 1, undefined
)).toEqual({ a : 1 })
})
test('null is considered an empty object', () => {
expect(assocPath(
'a', 1, null
)).toEqual({ a : 1 })
})
test('value can be null', () => {
expect(assocPath(
'a', null, null
)).toEqual({ a : null })
})
test('value can be undefined', () => {
expect(assocPath(
'a', undefined, null
)).toEqual({ a : undefined })
})
test('assignment is shallow', () => {
expect(assocPath(
'a', { b : 2 }, { a : { c : 3 } }
)).toEqual({ a : { b : 2 } })
})
test('empty array as path', () => {
const result = assocPath(
[], 3, {
a : 1,
b : 2,
}
)
expect(result).toBe(3)
})
test('happy', () => {
const expected = { foo : { bar : { baz : 42 } } }
const result = assocPath(
[ '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
})
})
```
[](#assocPath)
### binary
Try this R.binary example in Rambda REPL
[](#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}`
```
Try this R.bind example in Rambda REPL
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
})
})
```
[](#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]
```
Try this R.both example in Rambda REPL
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
})
})
```
[](#both)
### call
Try this R.call example in Rambda REPL
[](#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 ]
```
Try this R.chain example in Rambda REPL
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[]
})
})
```
[](#chain)
### clamp
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`.
Try this R.clamp example in Rambda REPL
[](#clamp)
### clone
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.
Try this R.clone example in Rambda REPL
[](#clone)
### collectBy
Try this R.collectBy example in Rambda REPL
[](#collectBy)
### comparator
It returns a comparator function that can be used in `sort` method.
Try this R.comparator example in Rambda REPL
[](#comparator)
### complement
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)`.
Try this R.complement example in Rambda REPL
[](#complement)
### compose
It performs right-to-left function composition.
Try this R.compose example in Rambda REPL
[](#compose)
### composeWith
Try this R.composeWith example in Rambda REPL
[](#composeWith)
### concat
It returns a new string or array, which is the result of merging `x` and `y`.
Try this R.concat example in Rambda REPL
[](#concat)
### cond
It takes list with `conditions` and returns a new function `fn` that expects `input` as argument.
This function will start evaluating the `conditions` in order to find the first winner(order of conditions matter).
The winner is this condition, which left side returns `true` when `input` is its argument. Then the evaluation of the right side of the winner will be the final result.
If no winner is found, then `fn` returns `undefined`.
Try this R.cond example in Rambda REPL
[](#cond)
### converge
Accepts a converging function and a list of branching functions and returns a new function. When invoked, this new function is applied to some arguments, each branching function is applied to those same arguments. The results of each branching function are passed as arguments to the converging function to produce the return value.
> :boom: Explanation is taken from `Ramda` documentation
Try this R.converge example in Rambda REPL
[](#converge)
### count
It counts how many times `predicate` function returns `true`, when supplied with iteration of `list`.
Try this R.count example in Rambda REPL
[](#count)
### countBy
```typescript
countBy(transformFn: (x: T) => any, list: T[]): Record
```
It counts elements in a list after each instance of the input list is passed through `transformFn` function.
```javascript
const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ]
const result = countBy(R.toLower, list)
const expected = { a: 2, b: 2, c: 2 }
// => `result` is equal to `expected`
```
Try this R.countBy example in Rambda REPL
All TypeScript definitions
```typescript
countBy(transformFn: (x: T) => any, list: T[]): Record;
countBy(transformFn: (x: T) => any): (list: T[]) => Record;
```
R.countBy source
```javascript
export function countBy(fn, list){
if (arguments.length === 1){
return _list => countBy(fn, _list)
}
const willReturn = {}
list.forEach(item => {
const key = fn(item)
if (!willReturn[ key ]){
willReturn[ key ] = 1
} else {
willReturn[ key ]++
}
})
return willReturn
}
```
Tests
```javascript
import { countBy } from './countBy.js'
const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ]
test('happy', () => {
const result = countBy(x => x.toLowerCase(), list)
expect(result).toEqual({
a : 2,
b : 2,
c : 2,
})
})
```
TypeScript test
```typescript
import {countBy} from 'rambda'
const transformFn = (x: string) => x.toLowerCase()
const list = ['a', 'A', 'b', 'B', 'c', 'C']
describe('R.countBy', () => {
it('happy', () => {
const result = countBy(transformFn, list)
result // $ExpectType Record
})
it('curried', () => {
const result = countBy(transformFn)(list)
result // $ExpectType Record
})
})
```
[](#countBy)
### curry
It expects a function as input and returns its curried version.
Try this R.curry example in Rambda REPL
[](#curry)
### curryN
It returns a curried equivalent of the provided function, with the specified arity.
[](#curryN)
### dec
It decrements a number.
Try this R.dec example in Rambda REPL
[](#dec)
### defaultTo
```typescript
defaultTo(defaultValue: T, input: T | null | undefined): T
```
It returns `defaultValue`, if all of `inputArguments` are `undefined`, `null` or `NaN`.
Else, it returns the first truthy `inputArguments` instance(from left to right).
> :boom: Rambda's **defaultTo** accept indefinite number of arguments when non curried, i.e. `R.defaultTo(2, foo, bar, baz)`.
```javascript
R.defaultTo('foo', 'bar') // => 'bar'
R.defaultTo('foo', undefined) // => 'foo'
// Important - emtpy string is not falsy value(same as Ramda)
R.defaultTo('foo', '') // => 'foo'
```
Try this R.defaultTo example in Rambda REPL
All TypeScript definitions
```typescript
defaultTo(defaultValue: T, input: T | null | undefined): T;
defaultTo(defaultValue: T): (input: T | null | undefined) => T;
```
R.defaultTo source
```javascript
function isFalsy(input){
return (
input === undefined || input === null || Number.isNaN(input) === true
)
}
export function defaultTo(defaultArgument, input){
if (arguments.length === 1){
return _input => defaultTo(defaultArgument, _input)
}
return isFalsy(input) ? defaultArgument : input
}
```
Tests
```javascript
import { defaultTo } from './defaultTo.js'
test('with undefined', () => {
expect(defaultTo('foo')(undefined)).toBe('foo')
})
test('with null', () => {
expect(defaultTo('foo')(null)).toBe('foo')
})
test('with NaN', () => {
expect(defaultTo('foo')(NaN)).toBe('foo')
})
test('with empty string', () => {
expect(defaultTo('foo', '')).toBe('')
})
test('with false', () => {
expect(defaultTo('foo', false)).toBeFalse()
})
test('when inputArgument passes initial check', () => {
expect(defaultTo('foo', 'bar')).toBe('bar')
})
```
TypeScript test
```typescript
import {defaultTo} from 'rambda'
describe('R.defaultTo with Ramda spec', () => {
it('happy', () => {
const result = defaultTo('foo', '')
result // $ExpectType "" | "foo"
})
it('with explicit type', () => {
const result = defaultTo('foo', null)
result // $ExpectType string
})
})
```
[](#defaultTo)
### descend
Try this R.descend example in Rambda REPL
[](#descend)
### difference
```typescript
difference(a: T[], b: T[]): T[]
```
It returns the uniq set of all elements in the first list `a` not contained in the second list `b`.
`R.equals` is used to determine equality.
```javascript
const a = [ 1, 2, 3, 4 ]
const b = [ 3, 4, 5, 6 ]
const result = R.difference(a, b)
// => [ 1, 2 ]
```
Try this R.difference example in Rambda REPL
All TypeScript definitions
```typescript
difference(a: T[], b: T[]): T[];
difference(a: T[]): (b: T[]) => T[];
```
R.difference source
```javascript
import { includes } from './includes.js'
import { uniq } from './uniq.js'
export function difference(a, b){
if (arguments.length === 1) return _b => difference(a, _b)
return uniq(a).filter(aInstance => !includes(aInstance, b))
}
```
Tests
```javascript
import { difference as differenceRamda } from 'ramda'
import { difference } from './difference.js'
test('difference', () => {
const a = [ 1, 2, 3, 4 ]
const b = [ 3, 4, 5, 6 ]
expect(difference(a)(b)).toEqual([ 1, 2 ])
expect(difference([], [])).toEqual([])
})
test('difference with objects', () => {
const a = [ { id : 1 }, { id : 2 }, { id : 3 }, { id : 4 } ]
const b = [ { id : 3 }, { id : 4 }, { id : 5 }, { id : 6 } ]
expect(difference(a, b)).toEqual([ { id : 1 }, { id : 2 } ])
})
test('no duplicates in first list', () => {
const M2 = [ 1, 2, 3, 4, 1, 2, 3, 4 ]
const N2 = [ 3, 3, 4, 4, 5, 5, 6, 6 ]
expect(difference(M2, N2)).toEqual([ 1, 2 ])
})
test('should use R.equals', () => {
expect(difference([ 1 ], [ 1 ])).toHaveLength(0)
expect(differenceRamda([ NaN ], [ NaN ])).toHaveLength(0)
})
```
TypeScript test
```typescript
import {difference} from 'rambda'
const list1 = [1, 2, 3]
const list2 = [1, 2, 4]
describe('R.difference', () => {
it('happy', () => {
const result = difference(list1, list2)
result // $ExpectType number[]
})
it('curried', () => {
const result = difference(list1)(list2)
result // $ExpectType number[]
})
})
```
[](#difference)
### differenceWith
```typescript
differenceWith(
pred: (a: T1, b: T2) => boolean,
list1: T1[],
list2: T2[],
): T1[]
```
```javascript
const result = R.differenceWith(
(a, b) => a.x === b.x,
[{x: 1}, {x: 2}],
[{x: 1}, {x: 3}]
)
// => [{x: 2}]
```
Try this R.differenceWith example in Rambda REPL
All TypeScript definitions
```typescript
differenceWith(
pred: (a: T1, b: T2) => boolean,
list1: T1[],
list2: T2[],
): T1[];
differenceWith(
pred: (a: T1, b: T2) => boolean,
): (list1: T1[], list2: T2[]) => T1[];
differenceWith(
pred: (a: T1, b: T2) => boolean,
list1: T1[],
): (list2: T2[]) => T1[];
```
R.differenceWith source
```javascript
import { curry } from './curry.js'
import { _indexOf } from './equals.js'
export function differenceWithFn(
fn, a, b
){
const willReturn = []
const [ first, second ] = a.length > b.length ? [ a, b ] : [ b, a ]
first.forEach(item => {
const hasItem = second.some(secondItem => fn(item, secondItem))
if (!hasItem && _indexOf(item, willReturn) === -1){
willReturn.push(item)
}
})
return willReturn
}
export const differenceWith = curry(differenceWithFn)
```
Tests
```javascript
import { differenceWith } from './differenceWith.js'
test('happy', () => {
const foo = [ { a : 1 }, { a : 2 }, { a : 3 } ]
const bar = [ { a : 3 }, { a : 4 } ]
const fn = function (r, s){
return r.a === s.a
}
const result = differenceWith(
fn, foo, bar
)
expect(result).toEqual([ { a : 1 }, { a : 2 } ])
})
```
[](#differenceWith)
### dissoc
It returns a new object that does not contain property `prop`.
Try this R.dissoc example in Rambda REPL
[](#dissoc)
### dissocPath
Try this R.dissocPath example in Rambda REPL
[](#dissocPath)
### divide
Try this R.divide example in Rambda REPL
[](#divide)
### drop
```typescript
drop(howMany: number, input: T[]): T[]
```
It returns `howMany` items dropped from beginning of list or string `input`.
```javascript
R.drop(2, ['foo', 'bar', 'baz']) // => ['baz']
R.drop(2, 'foobar') // => 'obar'
```
Try this R.drop example in Rambda REPL
All TypeScript definitions
```typescript
drop(howMany: number, input: T[]): T[];
drop(howMany: number, input: string): string;
drop(howMany: number): {
(input: T[]): T[];
(input: string): string;
};
```
R.drop source
```javascript
export function drop(howManyToDrop, listOrString){
if (arguments.length === 1) return _list => drop(howManyToDrop, _list)
return listOrString.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
```
Tests
```javascript
import assert from 'assert'
import { drop } from './drop.js'
test('with array', () => {
expect(drop(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ])
expect(drop(3, [ 'foo', 'bar', 'baz' ])).toEqual([])
expect(drop(4, [ 'foo', 'bar', 'baz' ])).toEqual([])
})
test('with string', () => {
expect(drop(3, 'rambda')).toBe('bda')
})
test('with non-positive count', () => {
expect(drop(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
expect(drop(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
expect(drop(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})
test('should return copy', () => {
const xs = [ 1, 2, 3 ]
assert.notStrictEqual(drop(0, xs), xs)
assert.notStrictEqual(drop(-1, xs), xs)
})
```
TypeScript test
```typescript
import {drop} from 'rambda'
const list = [1, 2, 3, 4]
const str = 'foobar'
const howMany = 2
describe('R.drop - array', () => {
it('happy', () => {
const result = drop(howMany, list)
result // $ExpectType number[]
})
it('curried', () => {
const result = drop(howMany)(list)
result // $ExpectType number[]
})
})
describe('R.drop - string', () => {
it('happy', () => {
const result = drop(howMany, str)
result // $ExpectType string
})
it('curried', () => {
const result = drop(howMany)(str)
result // $ExpectType string
})
})
```
[](#drop)
### dropLast
```typescript
dropLast(howMany: number, input: T[]): T[]
```
It returns `howMany` items dropped from the end of list or string `input`.
```javascript
R.dropLast(2, ['foo', 'bar', 'baz']) // => ['foo']
R.dropLast(2, 'foobar') // => 'foob'
```
Try this R.dropLast example in Rambda REPL
All TypeScript definitions
```typescript
dropLast(howMany: number, input: T[]): T[];
dropLast(howMany: number, input: string): string;
dropLast(howMany: number): {
(input: T[]): T[];
(input: string): string;
};
```
R.dropLast source
```javascript
export function dropLast(howManyToDrop, listOrString){
if (arguments.length === 1){
return _listOrString => dropLast(howManyToDrop, _listOrString)
}
return howManyToDrop > 0 ?
listOrString.slice(0, -howManyToDrop) :
listOrString.slice()
}
```
Tests
```javascript
import assert from 'assert'
import { dropLast } from './dropLast.js'
test('with array', () => {
expect(dropLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo' ])
expect(dropLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([])
expect(dropLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([])
})
test('with string', () => {
expect(dropLast(3, 'rambda')).toBe('ram')
})
test('with non-positive count', () => {
expect(dropLast(0, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
expect(dropLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
expect(dropLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})
test('should return copy', () => {
const xs = [ 1, 2, 3 ]
assert.notStrictEqual(dropLast(0, xs), xs)
assert.notStrictEqual(dropLast(-1, xs), xs)
})
```
TypeScript test
```typescript
import {dropLast} from 'rambda'
const list = [1, 2, 3, 4]
const str = 'foobar'
const howMany = 2
describe('R.dropLast - array', () => {
it('happy', () => {
const result = dropLast(howMany, list)
result // $ExpectType number[]
})
it('curried', () => {
const result = dropLast(howMany)(list)
result // $ExpectType number[]
})
})
describe('R.dropLast - string', () => {
it('happy', () => {
const result = dropLast(howMany, str)
result // $ExpectType string
})
it('curried', () => {
const result = dropLast(howMany)(str)
result // $ExpectType string
})
})
```
[](#dropLast)
### dropLastWhile
Try this R.dropLastWhile example in Rambda REPL
[](#dropLastWhile)
### dropRepeats
```typescript
dropRepeats(list: T[]): T[]
```
It removes any successive duplicates according to `R.equals`.
```javascript
const result = R.dropRepeats([
1,
1,
{a: 1},
{a:1},
1
])
// => [1, {a: 1}, 1]
```
Try this R.dropRepeats example in Rambda REPL
All TypeScript definitions
```typescript
dropRepeats(list: T[]): T[];
```
R.dropRepeats source
```javascript
import { isArray } from './_internals/isArray.js'
import { equals } from './equals.js'
export function dropRepeats(list){
if (!isArray(list)){
throw new Error(`${ list } is not a list`)
}
const toReturn = []
list.reduce((prev, current) => {
if (!equals(prev, current)){
toReturn.push(current)
}
return current
}, undefined)
return toReturn
}
```
Tests
```javascript
import { dropRepeats as dropRepeatsRamda } from 'ramda'
import { compareCombinations } from './_internals/testUtils.js'
import { add } from './add.js'
import { dropRepeats } from './dropRepeats.js'
const list = [ 1, 2, 2, 2, 3, 4, 4, 5, 5, 3, 2, 2, { a : 1 }, { a : 1 } ]
const listClean = [ 1, 2, 3, 4, 5, 3, 2, { a : 1 } ]
test('happy', () => {
const result = dropRepeats(list)
expect(result).toEqual(listClean)
})
const possibleLists = [
[ add(1), async () => {}, [ 1 ], [ 1 ], [ 2 ], [ 2 ] ],
[ add(1), add(1), add(2) ],
[],
1,
/foo/g,
Promise.resolve(1),
]
describe('brute force', () => {
compareCombinations({
firstInput : possibleLists,
callback : errorsCounters => {
expect(errorsCounters).toMatchInlineSnapshot(`
{
"ERRORS_MESSAGE_MISMATCH": 0,
"ERRORS_TYPE_MISMATCH": 0,
"RESULTS_MISMATCH": 1,
"SHOULD_NOT_THROW": 3,
"SHOULD_THROW": 0,
"TOTAL_TESTS": 6,
}
`)
},
fn : dropRepeats,
fnRamda : dropRepeatsRamda,
})
})
```
TypeScript test
```typescript
import {dropRepeats} from 'rambda'
describe('R.dropRepeats', () => {
it('happy', () => {
const result = dropRepeats([1, 2, 2, 3])
result // $ExpectType number[]
})
})
```
[](#dropRepeats)
### dropRepeatsBy
Try this R.dropRepeatsBy example in Rambda REPL
[](#dropRepeatsBy)
### dropRepeatsWith
Try this R.dropRepeatsWith example in Rambda REPL
[](#dropRepeatsWith)
### dropWhile
Try this R.dropWhile example in Rambda REPL
[](#dropWhile)
### either
```typescript
either(firstPredicate: Pred, secondPredicate: Pred): Pred
```
It returns a new `predicate` function from `firstPredicate` and `secondPredicate` inputs.
This `predicate` function will return `true`, if any of the two input predicates return `true`.
```javascript
const firstPredicate = x => x > 10
const secondPredicate = x => x % 2 === 0
const predicate = R.either(firstPredicate, secondPredicate)
const result = [
predicate(15),
predicate(8),
predicate(7),
]
// => [true, true, false]
```
Try this R.either example in Rambda REPL
All TypeScript definitions
```typescript
either(firstPredicate: Pred, secondPredicate: Pred): Pred;
either(firstPredicate: Predicate, secondPredicate: Predicate): Predicate;
either(firstPredicate: Predicate): (secondPredicate: Predicate) => Predicate;
either(firstPredicate: Pred): (secondPredicate: Pred) => Pred;
```
R.either source
```javascript
export function either(firstPredicate, secondPredicate){
if (arguments.length === 1){
return _secondPredicate => either(firstPredicate, _secondPredicate)
}
return (...input) =>
Boolean(firstPredicate(...input) || secondPredicate(...input))
}
```
Tests
```javascript
import { either } from './either.js'
test('with multiple inputs', () => {
const between = function (
a, b, c
){
return a < b && b < c
}