rambda
Version:
Lightweight and faster alternative to Ramda with included TS definitions
2,167 lines (1,528 loc) • 332 kB
Markdown
# Rambda
`Rambda` is TypeScript-focused utility library similar to `Remeda`, `Ramda` and `Radashi`. - [Documentation site](https://selfrefactor.github.io/rambda/#/)


[](https://packagephobia.com/result?p=rambda)
[](https://github.com/selfrefactor/rambda/pulls)
[](https://github.com/selfrefactor/rambda/graphs/contributors)
## ❯ Example use
```javascript
import { pipe, filter, map } from 'rambda'
const result = pipe(
[1, 2, 3, 4],
filter(x => x > 2),
map(x => x * 2),
)
//=> [6, 8]
```
You can test this example in <a href="https://rambda.netlify.app/?const%20result%20%3D%20R.pipe(%0A%20%20%5B1%2C%202%2C%203%2C%204%5D%2C%0A%20%20R.filter(x%20%3D%3E%20x%20%3E%202)%2C%0A%20%20R.map(x%20%3D%3E%20x%20*%202)%2C%0A)%0A%2F%2F%20%3D%3E%20%5B6%2C%208%5D">Rambda's REPL</a>
* [API](#api)
* [Changelog](#-changelog)
[](#-example-use)
## ❯ Rambda's features
## ❯ Goals
### Typescript focus
Mixing `Functional Programming` and `TypeScript` is not easy.
One way to solve this is to focus what can be actually achieved and refrain from what is not possible.
### `R.pipe` as the main way to use Rambda
- All methods are meant to be used as part of `R.pipe` chain
- This is the main purpose of functional programming, i.e. to pass data through a chain of functions.
- Having `R.pipe(input, ...fns)` helps TypeScript to infer the types of the input and the output.
Here is one example why `R.pipe` is better than `Ramda.pipe`:
```ts
const list = [1, 2, 3];
it('within pipe', () => {
const result = pipe(
list,
filter((x) => {
x; // $ExpectType number
return x > 1;
}),
);
result; // $ExpectType number[]
});
it('within Ramda.pipe requires explicit types', () => {
Ramda.pipe(
(x) => x,
filter<number>((x) => {
x; // $ExpectType number
return x > 1;
}),
filter((x: number) => {
x; // $ExpectType number
return x > 1;
}),
)(list);
});
```
:exclamation: IMPORTANT - all methods are tested to deliver correct types when they are part of `R.pipe/R.pipeAsync` chains.
In other words:
```typescript
R.filter(x => x > 1)([1,2,3])
```
might trigger TS error as it not the same as
```typescript
R.pipe([1,2,3], R.filter(x => x > 1)
```
### :exclamation: All methods are curried
There is one way to use `Rambda` methods and it is with currying, i.e. using `R.filter(fn, list)` will not work as it is inteded to be `R.filter(fn)(list)`.
The reason is that all methods are supposed to be used inside `R.pipe`. After all, building chains is the very base of functional programming.
Of course, there is value in supporting the case where you can pass all inputs at once, but I find that the price in terms of maintainability is not worth it.
### Keep only the most useful methods
The idea is to give `TypeScript` users only the most useful methods and let them implement the rest. No magic logic methods that are hard to remember. You shouldn't need to read the documentation to understand what a method does. Its name and signature should be enough.
- Methods that are simply to remember only by its name. Complex logic shouldn't be part of utility library, but part of your codebase.
- Keep only methods which are both useful and which behaviour is obvious from its name. For example, `R.innerJoin` is kept, but `R.identical`, `R.move` is removed. Methods such as `R.toLower`, `R.length` provide little value. Such method are omitted from Rambda on purpose.
- Some generic methods such as `curry` and `assoc` is not easy to be expressed in TypeScript. For this reason `Rambda` omits such methods.
- No `R.cond` or `R.ifElse` as they make the chain less readable.
- No `R.length` as it adds very little value.
- No `R.difference` as user must remember the order of the inputs, i.e. which is compared to and which is compared against.
### One way to use each method
Because of the focus on `R.pipe`, there is only one way to use each method. This helps with testing and also with TypeScript definitions.
- All methods that 2 inputs, will have to be called with `R.methodName(input1)(input2)`
- All methods that 3 inputs, will have to be called with `R.methodName(input1, input2)(input3)`
### Deno support
```
import * as R from "https://deno.land/x/rambda/mod.ts";
R.filter(x => x > 1)([1, 2, 3])
```
### Dot notation for `R.path`
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']`.
### 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
```
### Differences between Rambda and Ramda
Up until version `9.4.2`, the aim of Rambda was to match as much as possible the Ramda API.
You can find documentation site of **Rambda** version **9.4.2** is [here](https://selfrefactor.github.io/rambda-v9/).
From version `10.0.0` onwards, **Rambda** is no longer aiming to be drop-in replacement for *Ramda*.
[](#-rambdas-features)
## API
### addProp
```typescript
addProp<T extends object, P extends PropertyKey, V extends unknown>(
prop: P,
value: V
): (obj: T) => MergeTypes<T & Record<P, V>>
```
It adds new key-value pair to the object.
```javascript
const result = R.pipe(
{ a: 1, b: 'foo' },
R.addProp('c', 3)
)
// => { a: 1, b: 'foo', c: 3 }
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.pipe(%0A%09%7B%20a%3A%201%2C%20b%3A%20'foo'%20%7D%2C%0A%09R.addProp('c'%2C%203)%0A)%0A%2F%2F%20%3D%3E%20%7B%20a%3A%201%2C%20b%3A%20'foo'%2C%20c%3A%203%20%7D">Try this <strong>R.addProp</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
addProp<T extends object, P extends PropertyKey, V extends unknown>(
prop: P,
value: V
): (obj: T) => MergeTypes<T & Record<P, V>>;
```
</details>
<details>
<summary><strong>R.addProp</strong> source</summary>
```javascript
export function addProp(key, value) {
return obj => ({ ...obj, [key]: value })
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { addProp } from "./addProp.js"
test('happy', () => {
const result = addProp('a', 1)({ b: 2 })
const expected = { a: 1, b: 2 }
expect(result).toEqual(expected)
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { addProp, pipe } from 'rambda'
it('R.addProp', () => {
const result = pipe({ a: 1, b: 'foo' }, addProp('c', 3))
result.a // $ExpectType number
result.b // $ExpectType string
result.c // $ExpectType number
})
```
</details>
[](#addProp)
### addPropToObjects
```typescript
addPropToObjects<
T extends object,
K extends string,
R
>(
property: K,
fn: (input: T) => R
): (list: T[]) => MergeTypes<T & { [P in K]: R }>[]
```
It receives list of objects and add new property to each item.
The value is based on result of `fn` function, which receives the current object as argument.
```javascript
const result = R.pipe(
[
{a: 1, b: 2},
{a: 3, b: 4},
],
R.addPropToObjects(
'c',
(x) => String(x.a + x.b),
)
)
// => [{a: 1, b: 2, c: '3'}, {a: 3, b: 4, c: '7'}]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.pipe(%0A%09%5B%0A%09%09%7Ba%3A%201%2C%20b%3A%202%7D%2C%0A%09%09%7Ba%3A%203%2C%20b%3A%204%7D%2C%0A%09%5D%2C%0A%09R.addPropToObjects(%0A%09%09'c'%2C%0A%09%09(x)%20%3D%3E%20String(x.a%20%2B%20x.b)%2C%0A%09)%0A)%0A%2F%2F%20%3D%3E%20%5B%7Ba%3A%201%2C%20b%3A%202%2C%20c%3A%20'3'%7D%2C%20%7Ba%3A%203%2C%20b%3A%204%2C%20c%3A%20'7'%7D%5D">Try this <strong>R.addPropToObjects</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
addPropToObjects<
T extends object,
K extends string,
R
>(
property: K,
fn: (input: T) => R
): (list: T[]) => MergeTypes<T & { [P in K]: R }>[];
```
</details>
<details>
<summary><strong>R.addPropToObjects</strong> source</summary>
```javascript
import { mapFn } from './map.js'
export function addPropToObjects (
property,
fn
){
return listOfObjects => mapFn(
(obj) => ({
...(obj),
[property]: fn(obj)
}),
listOfObjects
)
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { pipe } from "./pipe.js"
import { addPropToObjects } from "./addPropToObjects.js"
test('R.addPropToObjects', () => {
let result = pipe(
[
{a: 1, b: 2},
{a: 3, b: 4},
],
addPropToObjects(
'c',
(x) => String(x.a + x.b),
)
)
expect(result).toEqual([
{ a: 1, b: 2, c: '3' },
{ a: 3, b: 4, c: '7' },
])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { addPropToObjects, pipe } from 'rambda'
it('R.addPropToObjects', () => {
let result = pipe(
[
{a: 1, b: 2},
{a: 3, b: 4},
],
addPropToObjects(
'c',
(x) => String(x.a + x.b),
)
)
result // $ExpectType { a: number; b: number; c: string; }[]
})
```
</details>
[](#addPropToObjects)
### all
```typescript
all<T>(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.pipe(
list,
R.all(predicate)
) // => true
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20list%20%3D%20%5B%200%2C%201%2C%202%2C%203%2C%204%20%5D%0Aconst%20predicate%20%3D%20x%20%3D%3E%20x%20%3E%20-1%0A%0Aconst%20result%20%3D%20R.pipe(%0A%09list%2C%0A%09R.all(predicate)%0A)%20%2F%2F%20%3D%3E%20true">Try this <strong>R.all</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
all<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
```
</details>
<details>
<summary><strong>R.all</strong> source</summary>
```javascript
export function all(predicate) {
return list => {
for (let i = 0; i < list.length; i++) {
if (!predicate(list[i])) {
return false
}
}
return true
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```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)).toBeTruthy()
})
test('when false', () => {
const fn = x => x > 2
expect(all(fn)(list)).toBeFalsy()
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import * as R from 'rambda'
describe('all', () => {
it('happy', () => {
const result = R.pipe(
[1, 2, 3],
R.all(x => {
x // $ExpectType number
return x > 0
}),
)
result // $ExpectType boolean
})
})
```
</details>
[](#all)
### allPass
```typescript
allPass<F extends (...args: any[]) => boolean>(predicates: readonly F[]): F
```
It returns `true`, if all functions of `predicates` return `true`, when `input` is their argument.
```javascript
const list = [[1, 2, 3, 4], [3, 4, 5]]
const result = R.pipe(
list,
R.filter(R.allPass([R.includes(2), R.includes(3)]))
) // => [[1, 2, 3, 4]]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20list%20%3D%20%5B%5B1%2C%202%2C%203%2C%204%5D%2C%20%5B3%2C%204%2C%205%5D%5D%0Aconst%20result%20%3D%20R.pipe(%0A%09list%2C%0A%09R.filter(R.allPass(%5BR.includes(2)%2C%20R.includes(3)%5D))%0A)%20%2F%2F%20%3D%3E%20%5B%5B1%2C%202%2C%203%2C%204%5D%5D">Try this <strong>R.allPass</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
allPass<F extends (...args: any[]) => boolean>(predicates: readonly F[]): F;
```
</details>
<details>
<summary><strong>R.allPass</strong> source</summary>
```javascript
export function allPass(predicates) {
return input => {
let counter = 0
while (counter < predicates.length) {
if (!predicates[counter](input)) {
return false
}
counter++
}
return true
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { allPass } from './allPass.js'
import { filter } from './filter.js'
import { pipe } from './pipe.js'
const list = [
[1, 2, 3, 4],
[3, 4, 5],
]
test('happy', () => {
const result = pipe(list, filter(allPass([x => x.includes(2), x => x.includes(3)])))
expect(result).toEqual([[1, 2, 3, 4]])
})
test('when returns false', () => {
const result = pipe(list, filter(allPass([x => x.includes(12), x => x.includes(31)])))
expect(result).toEqual([])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import * as R from 'rambda'
describe('allPass', () => {
it('happy', () => {
const list = [
[1, 2, 3, 4],
[3, 4, 5],
]
const result = R.pipe(list, R.map(R.allPass([
(x) => x.length > 2,
(x) => x.includes(3)
])))
result // $ExpectType boolean[]
})
})
```
</details>
[](#allPass)
### any
```typescript
any<T>(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(predicate)(list)
// => true
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20list%20%3D%20%5B1%2C%202%2C%203%5D%0Aconst%20predicate%20%3D%20x%20%3D%3E%20x%20*%20x%20%3E%208%0Aconst%20result%20%3D%20R.any(predicate)(list)%0A%2F%2F%20%3D%3E%20true">Try this <strong>R.any</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
any<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
```
</details>
<details>
<summary><strong>R.any</strong> source</summary>
```javascript
export function any(predicate) {
return list => {
let counter = 0
while (counter < list.length) {
if (predicate(list[counter], counter)) {
return true
}
counter++
}
return false
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { any } from './any.js'
const list = [1, 2, 3]
test('happy', () => {
expect(any(x => x > 2)(list)).toBeTruthy()
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { any, pipe } from 'rambda'
it('R.any', () => {
const result = pipe(
[1, 2, 3],
any(x => {
x // $ExpectType number
return x > 2
}),
)
result // $ExpectType boolean
})
```
</details>
[](#any)
### anyPass
```typescript
anyPass<T, TF1 extends T, TF2 extends T>(
predicates: [(a: T) => a is TF1, (a: T) => a is TF2],
): (a: T) => a is TF1 | TF2
```
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
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20isBig%20%3D%20x%20%3D%3E%20x%20%3E%2020%0Aconst%20isOdd%20%3D%20x%20%3D%3E%20x%20%25%202%20%3D%3D%3D%201%0Aconst%20input%20%3D%2011%0A%0Aconst%20fn%20%3D%20R.anyPass(%0A%20%20%5BisBig%2C%20isOdd%5D%0A)%0A%0Aconst%20result%20%3D%20fn(input)%0A%2F%2F%20%3D%3E%20true">Try this <strong>R.anyPass</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
anyPass<T, TF1 extends T, TF2 extends T>(
predicates: [(a: T) => a is TF1, (a: T) => a is TF2],
): (a: T) => a is TF1 | TF2;
anyPass<T, TF1 extends T, TF2 extends T, TF3 extends T>(
predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3],
): (a: T) => a is TF1 | TF2 | TF3;
anyPass<T, TF1 extends T, TF2 extends T, TF3 extends T>(
predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3],
): (a: T) => a is TF1 | TF2 | TF3;
anyPass<T, TF1 extends T, TF2 extends T, TF3 extends T, TF4 extends T>(
predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3, (a: T) => a is TF4],
): (a: T) => a is TF1 | TF2 | TF3 | TF4;
...
...
```
</details>
<details>
<summary><strong>R.anyPass</strong> source</summary>
```javascript
export function anyPass(predicates) {
return input => {
let counter = 0
while (counter < predicates.length) {
if (predicates[counter](input)) {
return true
}
counter++
}
return false
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { anyPass } from './anyPass.js'
test('happy', () => {
const rules = [x => typeof x === 'string', x => x > 10]
const predicate = anyPass(rules)
expect(predicate('foo')).toBeTruthy()
expect(predicate(6)).toBeFalsy()
})
test('happy', () => {
const rules = [x => typeof x === 'string', x => x > 10]
expect(anyPass(rules)(11)).toBeTruthy()
expect(anyPass(rules)(undefined)).toBeFalsy()
})
const obj = {
a: 1,
b: 2,
}
test('when returns true', () => {
const conditionArr = [val => val.a === 1, val => val.a === 2]
expect(anyPass(conditionArr)(obj)).toBeTruthy()
})
test('when returns false', () => {
const conditionArr = [val => val.a === 2, val => val.b === 3]
expect(anyPass(conditionArr)(obj)).toBeFalsy()
})
test('with empty predicates list', () => {
expect(anyPass([])(3)).toBeFalsy()
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { anyPass, filter } from 'rambda'
describe('anyPass', () => {
it('issue #604', () => {
const plusEq = (w: number, x: number, y: number, z: number) => 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])
const aValue: unknown = 1
if (isStringNumberOrBoolean(aValue)) {
aValue // $ExpectType string | number | boolean
}
})
})
```
</details>
[](#anyPass)
### append
```typescript
append<T>(el: T): (list: readonly T[]) => T[]
```
It adds element `x` at the end of `iterable`.
```javascript
const result = R.append('foo')(['bar', 'baz'])
// => ['bar', 'baz', 'foo']
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.append('foo')(%5B'bar'%2C%20'baz'%5D)%0A%2F%2F%20%3D%3E%20%5B'bar'%2C%20'baz'%2C%20'foo'%5D">Try this <strong>R.append</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
append<T>(el: T): (list: readonly T[]) => T[];
append<T>(el: T): (list: T[]) => T[];
```
</details>
<details>
<summary><strong>R.append</strong> source</summary>
```javascript
import { cloneList } from './_internals/cloneList.js'
export function append(x) {
return list => {
const clone = cloneList(list)
clone.push(x)
return clone
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```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'])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { append, pipe, prepend } from 'rambda'
const listOfNumbers = [1, 2, 3]
describe('R.append/R.prepend', () => {
it('happy', () => {
const result = pipe(listOfNumbers, append(4), prepend(0))
result // $ExpectType number[]
})
it('with object', () => {
const result = pipe([{ a: 1 }], append({ a: 10 }), prepend({ a: 20 }))
result // $ExpectType { a: number; }[]
})
})
```
</details>
[](#append)
### ascend
```typescript
ascend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering
```
Helper function to be used with `R.sort` to sort list in ascending order.
```javascript
const result = R.pipe(
[{a: 1}, {a: 2}, {a: 0}],
R.sort(R.ascend(R.prop('a')))
)
// => [{a: 0}, {a: 1}, {a: 2}]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.pipe(%0A%09%5B%7Ba%3A%201%7D%2C%20%7Ba%3A%202%7D%2C%20%7Ba%3A%200%7D%5D%2C%0A%09R.sort(R.ascend(R.prop('a')))%0A)%0A%2F%2F%20%3D%3E%20%5B%7Ba%3A%200%7D%2C%20%7Ba%3A%201%7D%2C%20%7Ba%3A%202%7D%5D">Try this <strong>R.ascend</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
ascend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
```
</details>
<details>
<summary><strong>R.ascend</strong> source</summary>
```javascript
export function createCompareFunction(a, b, winner, loser) {
if (a === b) {
return 0
}
return a < b ? winner : loser
}
export function ascend(getFunction) {
return (a, b) => {
const aValue = getFunction(a)
const bValue = getFunction(b)
return createCompareFunction(aValue, bValue, -1, 1)
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { ascend } from './ascend.js'
import { descend } from './descend.js'
import { sort } from './sort.js'
test('ascend', () => {
const result = sort(
ascend(x => x.a))(
[{a:1}, {a:3}, {a:2}],
)
expect(result).toEqual([{a:1}, {a:2}, {a:3}])
})
test('descend', () => {
const result = sort(
descend(x => x.a))(
[{a:1}, {a:3}, {a:2}],
)
expect(result).toEqual([{a:3}, {a:2}, {a:1}])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { pipe, ascend, sort } from 'rambda'
it('R.ascend', () => {
const result = pipe(
[{a:1}, {a:2}],
sort(ascend(x => x.a))
)
result // $ExpectType { a: number; }[]
})
```
</details>
[](#ascend)
### assertType
```typescript
assertType<T, U extends T>(fn: (x: T) => x is U) : (x: T) => U
```
It helps to make sure that input is from specific type. Similar to `R.convertToType`, but it actually checks the type of the input value. If `fn` input returns falsy value, then the function will throw an error.
<details>
<summary>All TypeScript definitions</summary>
```typescript
assertType<T, U extends T>(fn: (x: T) => x is U) : (x: T) => U;
```
</details>
<details>
<summary><strong>R.assertType</strong> source</summary>
```javascript
export function assertType(fn) {
return (x) => {
if (fn(x)) {
return x
}
throw new Error('type assertion failed in R.assertType')
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { assertType } from './assertType.js'
import { pipe } from './pipe.js'
test('happy', () => {
const result = pipe(
[1, 2, 3],
assertType((x) => x.length === 3),
)
expect(result).toEqual([1, 2, 3])
})
test('throw', () => {
expect(() => {
pipe(
[1, 2, 3],
assertType((x) => x.length === 4),
)
}).toThrow('type assertion failed in R.assertType')
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { pipe, assertType } from 'rambda'
type Book = {
title: string
year: number
}
type BookToRead = Book & {
bookmarkFlag: boolean
}
function isBookToRead(book: Book): book is BookToRead {
return (book as BookToRead).bookmarkFlag !== undefined
}
it('R.assertType', () => {
const result = pipe(
{ title: 'Book1', year: 2020, bookmarkFlag: true },
assertType(isBookToRead),
)
result // $ExpectType BookToRead
})
```
</details>
[](#assertType)
### checkObjectWithSpec
```typescript
checkObjectWithSpec<T>(spec: T): <U>(testObj: U) => boolean
```
It returns `true` if all each property in `conditions` returns `true` when applied to corresponding property in `input` object.
```javascript
const condition = R.checkObjectWithSpec({
a : x => typeof x === "string",
b : x => x === 4
})
const input = {
a : "foo",
b : 4,
c : 11,
}
const result = condition(input)
// => true
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20condition%20%3D%20R.checkObjectWithSpec(%7B%0A%20%20a%20%3A%20x%20%3D%3E%20typeof%20x%20%3D%3D%3D%20%22string%22%2C%0A%20%20b%20%3A%20x%20%3D%3E%20x%20%3D%3D%3D%204%0A%7D)%0Aconst%20input%20%3D%20%7B%0A%20%20a%20%3A%20%22foo%22%2C%0A%20%20b%20%3A%204%2C%0A%20%20c%20%3A%2011%2C%0A%7D%0A%0Aconst%20result%20%3D%20condition(input)%0A%2F%2F%20%3D%3E%20true">Try this <strong>R.checkObjectWithSpec</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
checkObjectWithSpec<T>(spec: T): <U>(testObj: U) => boolean;
```
</details>
<details>
<summary><strong>R.checkObjectWithSpec</strong> source</summary>
```javascript
export function checkObjectWithSpec(conditions) {
return input => {
let shouldProceed = true
for (const prop in conditions) {
if (!shouldProceed) {
continue
}
const result = conditions[prop](input[prop])
if (shouldProceed && result === false) {
shouldProceed = false
}
}
return shouldProceed
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { checkObjectWithSpec } from './checkObjectWithSpec.js'
import { equals } from './equals.js'
test('when true', () => {
const result = checkObjectWithSpec({
a: equals('foo'),
b: equals('bar'),
})({
a: 'foo',
b: 'bar',
x: 11,
y: 19,
})
expect(result).toBeTruthy()
})
test('when false | early exit', () => {
let counter = 0
const equalsFn = expected => input => {
counter++
return input === expected
}
const predicate = checkObjectWithSpec({
a: equalsFn('foo'),
b: equalsFn('baz'),
})
expect(
predicate({
a: 'notfoo',
b: 'notbar',
}),
).toBeFalsy()
expect(counter).toBe(1)
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { checkObjectWithSpec, equals } from 'rambda'
describe('R.checkObjectWithSpec', () => {
it('happy', () => {
const input = {
a: 'foo',
b: 'bar',
x: 11,
y: 19,
}
const conditions = {
a: equals('foo'),
b: equals('bar'),
}
const result = checkObjectWithSpec(conditions)(input)
result // $ExpectType boolean
})
})
```
</details>
[](#checkObjectWithSpec)
### compact
```typescript
compact<T>(list: T[]): Array<StrictNonNullable<T>>
```
It removes `null` and `undefined` members from list or object input.
```javascript
const result = R.pipe(
{
a: [ undefined, '', 'a', 'b', 'c'],
b: [1,2, null, 0, undefined, 3],
c: { a: 1, b: 2, c: 0, d: undefined, e: null, f: false },
},
x => ({
a: R.compact(x.a),
b: R.compact(x.b),
c: R.compact(x.c)
})
)
// => { a: ['a', 'b', 'c'], b: [1, 2, 3], c: { a: 1, b: 2, c: 0, f: false } }
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.pipe(%0A%09%7B%0A%09%09a%3A%20%5B%20undefined%2C%20''%2C%20'a'%2C%20'b'%2C%20'c'%5D%2C%0A%09%09b%3A%20%5B1%2C2%2C%20null%2C%200%2C%20undefined%2C%203%5D%2C%0A%09%09c%3A%20%7B%20a%3A%201%2C%20b%3A%202%2C%20c%3A%200%2C%20d%3A%20undefined%2C%20e%3A%20null%2C%20f%3A%20false%20%7D%2C%0A%09%7D%2C%0A%09x%20%3D%3E%20(%7B%0A%09%09a%3A%20R.compact(x.a)%2C%0A%09%09b%3A%20R.compact(x.b)%2C%0A%09%09c%3A%20R.compact(x.c)%0A%09%7D)%0A)%0A%2F%2F%20%3D%3E%20%7B%20a%3A%20%5B'a'%2C%20'b'%2C%20'c'%5D%2C%20b%3A%20%5B1%2C%202%2C%203%5D%2C%20c%3A%20%7B%20a%3A%201%2C%20b%3A%202%2C%20c%3A%200%2C%20f%3A%20false%20%7D%20%7D">Try this <strong>R.compact</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
compact<T>(list: T[]): Array<StrictNonNullable<T>>;
compact<T extends object>(record: T): {
[K in keyof T as Exclude<T[K], null | undefined> extends never
? never
: K
]: Exclude<T[K], null | undefined>
};
```
</details>
<details>
<summary><strong>R.compact</strong> source</summary>
```javascript
import { isArray } from './_internals/isArray.js'
import { reject } from './reject.js'
import { rejectObject } from './rejectObject.js'
const isNullOrUndefined = x => x === null || x === undefined
export function compact(input){
if(isArray(input)){
return reject(isNullOrUndefined)(input)
}
return rejectObject(isNullOrUndefined)(input)
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { compact } from './compact.js'
import { pipe } from './pipe.js'
test('happy', () => {
const result = pipe(
{
a: [ undefined, 'a', 'b', 'c'],
b: [1,2, null, 0, undefined, 3],
c: { a: 1, b: 2, c: 0, d: undefined, e: null, f: false },
},
x => ({
a: compact(x.a),
b: compact(x.b),
c: compact(x.c)
})
)
expect(result.a).toEqual(['a', 'b', 'c'])
expect(result.b).toEqual([1,2,0,3])
expect(result.c).toEqual({ a: 1, b: 2,c:0, f: false })
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { compact, pipe } from 'rambda'
it('R.compact', () => {
let result = pipe(
{
a: [ undefined, '', 'a', 'b', 'c', null ],
b: [1,2, null, 0, undefined, 3],
c: { a: 1, b: 2, c: 0, d: undefined, e: null, f: false },
},
x => ({
a: compact(x.a),
b: compact(x.b),
c: compact(x.c)
})
)
result.a // $ExpectType string[]
result.b // $ExpectType number[]
result.c // $ExpectType { a: number; b: number; c: number; f: boolean; }
})
```
</details>
[](#compact)
### complement
```typescript
complement<T extends any[]>(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 fn = x => x > 5
const inverted = complement(fn)
const result = [
fn(7),
inverted(7)
] => [ true, false ]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20fn%20%3D%20x%20%3D%3E%20x%20%3E%205%0Aconst%20inverted%20%3D%20complement(fn)%0A%0Aconst%20result%20%3D%20%5B%0A%20%20fn(7)%2C%0A%20%20inverted(7)%0A%5D%20%3D%3E%20%5B%20true%2C%20false%20%5D">Try this <strong>R.complement</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
complement<T extends any[]>(predicate: (...args: T) => unknown): (...args: T) => boolean;
```
</details>
<details>
<summary><strong>R.complement</strong> source</summary>
```javascript
export function complement(fn) {
return (...input) => !fn(...input)
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { complement } from './complement.js'
test('happy', () => {
const fn = complement(x => x.length === 0)
expect(fn([1, 2, 3])).toBeTruthy()
})
test('with multiple parameters', () => {
const between = (a, b, c) => a < b && b < c
const f = complement(between)
expect(f(4, 5, 11)).toBeFalsy()
expect(f(12, 2, 6)).toBeTruthy()
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { complement } from 'rambda'
describe('R.complement', () => {
it('happy', () => {
const fn = complement((x: number) => x > 10)
const result = fn(1)
result // $ExpectType boolean
})
})
```
</details>
[](#complement)
### concat
```typescript
concat<T>(x: T[]): (y: T[]) => T[]
```
It returns a new string or array, which is the result of merging `x` and `y`.
```javascript
R.concat([1, 2])([3, 4]) // => [1, 2, 3, 4]
R.concat('foo')('bar') // => 'foobar'
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?R.concat(%5B1%2C%202%5D)(%5B3%2C%204%5D)%20%2F%2F%20%3D%3E%20%5B1%2C%202%2C%203%2C%204%5D%0Aconst%20result%20%3D%20R.concat('foo')('bar')%20%2F%2F%20%3D%3E%20'foobar'">Try this <strong>R.concat</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
concat<T>(x: T[]): (y: T[]) => T[];
concat(x: string): (y: string) => string;
```
</details>
<details>
<summary><strong>R.concat</strong> source</summary>
```javascript
export function concat(x) {
return y => (typeof x === 'string' ? `${x}${y}` : [...x, ...y])
}
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { concat, pipe } from 'rambda'
const list1 = [1, 2, 3]
const list2 = [4, 5, 6]
it('R.concat', () => {
const result = pipe(list1, concat(list2))
result // $ExpectType number[]
const resultString = pipe('foo', concat('list2'))
resultString // $ExpectType string
})
```
</details>
[](#concat)
### convertToType
```typescript
convertToType<T>(x: unknown) : T
```
It helps to convert a value to a specific type.
It is useful when you have to overcome TypeScript's type inference.
<details>
<summary>All TypeScript definitions</summary>
```typescript
convertToType<T>(x: unknown) : T;
```
</details>
<details>
<summary><strong>R.convertToType</strong> source</summary>
```javascript
export function convertToType(x) {
return x
}
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { convertToType, pipe } from 'rambda'
const list = [1, 2, 3]
it('R.convertToType', () => {
const result = pipe(list,
convertToType<string[]>,
x => {
x // $ExpectType string[]
return x
}
)
result // $ExpectType string[]
})
```
</details>
[](#convertToType)
### count
```typescript
count<T>(predicate: (x: T) => boolean): (list: T[]) => number
```
It counts how many times `predicate` function returns `true`, when supplied with iteration of `list`.
```javascript
const list = [{a: 1}, 1, {a:2}]
const result = R.count(x => x.a !== undefined)(list)
// => 2
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20list%20%3D%20%5B%7Ba%3A%201%7D%2C%201%2C%20%7Ba%3A2%7D%5D%0Aconst%20result%20%3D%20R.count(x%20%3D%3E%20x.a%20!%3D%3D%20undefined)(list)%0A%2F%2F%20%3D%3E%202">Try this <strong>R.count</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
count<T>(predicate: (x: T) => boolean): (list: T[]) => number;
```
</details>
<details>
<summary><strong>R.count</strong> source</summary>
```javascript
import { isArray } from './_internals/isArray.js'
export function count(predicate) {
return list => {
if (!isArray(list)) {
return 0
}
return list.filter(x => predicate(x)).length
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { count } from './count.js'
const predicate = x => x.a !== undefined
test('with empty list', () => {
expect(count(predicate)([])).toBe(0)
})
test('happy', () => {
const list = [1, 2, { a: 1 }, 3, { a: 1 }]
expect(count(predicate)(list)).toBe(2)
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { count, pipe } from 'rambda'
const list = [1, 2, 3]
const predicate = (x: number) => x > 1
it('R.count', () => {
const result = pipe(list, count(predicate))
result // $ExpectType number
})
```
</details>
[](#count)
### countBy
```typescript
countBy<T>(fn: (x: T) => string | number): (list: T[]) => { [index: string]: number }
```
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(x => x.toLowerCase())( list)
const expected = { a: 2, b: 2, c: 2 }
// => `result` is equal to `expected`
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20list%20%3D%20%5B%20'a'%2C%20'A'%2C%20'b'%2C%20'B'%2C%20'c'%2C%20'C'%20%5D%0A%0Aconst%20result%20%3D%20countBy(x%20%3D%3E%20x.toLowerCase())(%20list)%0Aconst%20expected%20%3D%20%7B%20a%3A%202%2C%20b%3A%202%2C%20c%3A%202%20%7D%0A%2F%2F%20%3D%3E%20%60result%60%20is%20equal%20to%20%60expected%60">Try this <strong>R.countBy</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
countBy<T>(fn: (x: T) => string | number): (list: T[]) => { [index: string]: number };
```
</details>
<details>
<summary><strong>R.countBy</strong> source</summary>
```javascript
export function countBy(fn) {
return list => {
const willReturn = {}
list.forEach(item => {
const key = fn(item)
if (!willReturn[key]) {
willReturn[key] = 1
} else {
willReturn[key]++
}
})
return willReturn
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```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,
})
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { countBy, pipe } from 'rambda'
const list = ['a', 'A', 'b', 'B', 'c', 'C']
it('R.countBy', () => {
const result = pipe(
list,
countBy(x => x.toLowerCase()),
)
result.a // $ExpectType number
result.foo // $ExpectType number
result // $ExpectType { [index: string]: number; }
})
```
</details>
[](#countBy)
### createObjectFromKeys
```typescript
createObjectFromKeys<const K extends readonly PropertyKey[], V>(
fn: (key: K[number]) => V
): (keys: K) => { [P in K[number]]: V }
```
```javascript
const result = R.createObjectFromKeys(
(x, index) => `${x}-${index}`
)(['a', 'b', 'c'])
// => {a: 'a-0', b: 'b-1', c: 'c-2'}
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.createObjectFromKeys(%0A%09(x%2C%20index)%20%3D%3E%20%60%24%7Bx%7D-%24%7Bindex%7D%60%0A)(%5B'a'%2C%20'b'%2C%20'c'%5D)%0A%2F%2F%20%3D%3E%20%7Ba%3A%20'a-0'%2C%20b%3A%20'b-1'%2C%20c%3A%20'c-2'%7D">Try this <strong>R.createObjectFromKeys</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
createObjectFromKeys<const K extends readonly PropertyKey[], V>(
fn: (key: K[number]) => V
): (keys: K) => { [P in K[number]]: V };
createObjectFromKeys<const K extends readonly PropertyKey[], V>(
fn: (key: K[number], index: number) => V
): (keys: K) => { [P in K[number]]: V };
```
</details>
<details>
<summary><strong>R.createObjectFromKeys</strong> source</summary>
```javascript
export function createObjectFromKeys(fn) {
return keys => {
const result = {}
keys.forEach((key, index) => {
result[key] = fn(key, index)
})
return result
}
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { createObjectFromKeys } from './createObjectFromKeys.js'
test('happy', () => {
const result = createObjectFromKeys((key, index) => key.toUpperCase() + index)(['a', 'b'])
const expected = { a: 'A0', b: 'B1' }
expect(result).toEqual(expected)
})
```
</details>
[](#createObjectFromKeys)
### defaultTo
```typescript
defaultTo<T>(defaultValue: T): (input: unknown) => 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: Typescript Note: Pass explicit type annotation when used with **R.pipe/R.compose** for better type inference
```javascript
R.defaultTo('foo')('bar') // => 'bar'
R.defaultTo('foo'))(undefined) // => 'foo'
// Important - emtpy string is not falsy value
R.defaultTo('foo')('') // => 'foo'
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?R.defaultTo('foo')('bar')%20%2F%2F%20%3D%3E%20'bar'%0AR.defaultTo('foo'))(undefined)%20%2F%2F%20%3D%3E%20'foo'%0A%0A%2F%2F%20Important%20-%20emtpy%20string%20is%20not%20falsy%20value%0Aconst%20result%20%3D%20R.defaultTo('foo')('')%20%2F%2F%20%3D%3E%20'foo'">Try this <strong>R.defaultTo</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
defaultTo<T>(defaultValue: T): (input: unknown) => T;
```
</details>
<details>
<summary><strong>R.defaultTo</strong> source</summary>
```javascript
function isFalsy(input) {
return input === undefined || input === null || Number.isNaN(input) === true
}
export function defaultTo(defaultArgument) {
return input => isFalsy(input) ? defaultArgument : input
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```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')(Number.NaN)).toBe('foo')
})
test('with empty string', () => {
expect(defaultTo('foo')('')).toBe('')
})
test('with false', () => {
expect(defaultTo('foo')(false)).toBeFalsy()
})
test('when inputArgument passes initial check', () => {
expect(defaultTo('foo')('bar')).toBe('bar')
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { defaultTo, pipe } from 'rambda'
describe('R.defaultTo', () => {
it('happy', () => {
const result = pipe('bar' as unknown, defaultTo('foo'))
result // $ExpectType string
})
})
```
</details>
[](#defaultTo)
### descend
```typescript
descend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering
```
Helper function to be used with `R.sort` to sort list in descending order.
```javascript
const result = R.pipe(
[{a: 1}, {a: 2}, {a: 0}],
R.sort(R.descend(R.prop('a')))
)
// => [{a: 2}, {a: 1}, {a: 0}]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.pipe(%0A%09%5B%7Ba%3A%201%7D%2C%20%7Ba%3A%202%7D%2C%20%7Ba%3A%200%7D%5D%2C%0A%09R.sort(R.descend(R.prop('a')))%0A)%0A%2F%2F%20%3D%3E%20%5B%7Ba%3A%202%7D%2C%20%7Ba%3A%201%7D%2C%20%7Ba%3A%200%7D%5D">Try this <strong>R.descend</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
descend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
```
</details>
<details>
<summary><strong>R.descend</strong> source</summary>
```javascript
import { createCompareFunction } from './ascend.js'
export function descend(getFunction) {
return (a, b) => {
const aValue = getFunction(a)
const bValue = getFunction(b)
return createCompareFunction(aValue, bValue, 1, -1)
}
}
```
</details>
[](#descend)
### difference
```typescript
difference<T>(x: T[]): (y: T[]) => T[]
```
It returns a merged list of `x` and `y` with all equal elements removed.
`R.equals` is used to determine equality.
```javascript
const x = [ 1, 2, 3, 4 ]
const y = [ 3, 4, 5, 6 ]
const result = R.difference(x)(y)
// => [ 1, 2, 5, 6 ]
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20x%20%3D%20%5B%201%2C%202%2C%203%2C%204%20%5D%0Aconst%20y%20%3D%20%5B%203%2C%204%2C%205%2C%206%20%5D%0A%0Aconst%20result%20%3D%20R.difference(x)(y)%0A%2F%2F%20%3D%3E%20%5B%201%2C%202%2C%205%2C%206%20%5D">Try this <strong>R.difference</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
difference<T>(x: T[]): (y: T[]) => T[];
```
</details>
<details>
<summary><strong>R.difference</strong> source</summary>
```javascript
import { filter } from './filter.js'
import { excludes } from './excludes.js'
export function difference(listA) {
return listB => ([
...filter(value => excludes(listB)(value))(listA),
...filter(value => excludes(listA)(value))(listB),
])
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
import { difference } from './difference.js'
test('difference', () => {
const list1 = [1, 2, 3, 4]
const list2 = [3, 4, 5, 6]
expect(difference(list1)(list2)).toEqual([1, 2, 5, 6])
expect(difference([])([])).toEqual([])
})
test('difference with objects', () => {
const list1 = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]
const list2 = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }]
expect(difference(list1)(list2)).toEqual([
{ id: 1 },
{ id: 2 },
{ id: 5 },
{ id: 6 },
])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { difference } from 'rambda'
describe('R.difference', () => {
it('happy', () => {
const list1 = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]
const list2 = [{ id: 3 }, { id: 4 }, { id: 5 }, { id: 6 }]
const result = difference(list1)(list2)
result // $ExpectType { id: number; }[]
})
})
```
</details>
[](#difference)
### drop
```typescript
drop<T>(howMany: number): (list: T[]) => T[]
```
It returns `howMany` items dropped from beginning of list.
```javascript
R.drop(2)(['foo', 'bar', 'baz']) // => ['baz']
```
<a title="redirect to Rambda Repl site" href="https://rambda.netlify.app?const%20result%20%3D%20R.drop(2)(%5B'foo'%2C%20'bar'%2C%20'baz'%5D)%20%2F%2F%20%3D%3E%20%5B'baz'%5D">Try this <strong>R.drop</strong> example in Rambda REPL</a>
<details>
<summary>All TypeScript definitions</summary>
```typescript
drop<T>(howMany: number): (list: T[]) => T[];
```
</details>
<details>
<summary><strong>R.drop</strong> source</summary>
```javascript
export function drop(howManyToDrop, ) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
```
</details>
<details>
<summary><strong>Tests</strong></summary>
```javascript
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 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(Number.NEGATIVE_INFINITY)([1, 2, 3])).toEqual([1, 2, 3])
})
```
</details>
<details>
<summary><strong>TypeScript</strong> test</summary>
```typescript
import { drop, pipe } from 'rambda'
it('R.drop', () => {
const result = pipe([1, 2, 3, 4], drop(2))
result // $ExpectType number[]
})
```
</details>
[![---------------](https://raw.githubusercontent.com/selfrefactor/rambda/master/f