Lightweight and faster alternative to Ramda with included TS definitions
npm install rambdaRambda is TypeScript-focused utility library similar to Remeda, Ramda and Radashi. - Documentation site
!Commit activity
!Library size



``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 Rambda's REPL

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.
- 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
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)
`
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.
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.
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)R.methodName(input1, input2)(input3)
- All methods that 3 inputs, will have to be called with
`
import * as R from "https://deno.land/x/rambda/mod.ts";
R.filter(x => x > 1)([1, 2, 3])
`
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'].
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
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.
From version 10.0.0 onwards, Rambda is no longer aiming to be drop-in replacement for Ramda.

`typescript
addProp
prop: P,
value: V
): (obj: T) => MergeTypes
`
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 }
Try this R.addProp example in Rambda REPL
`typescript`
addProp
prop: P,
value: V
): (obj: T) => MergeTypes
`javascript`
export function addProp(key, value) {
return obj => ({ ...obj, [key]: value })
}
`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)
})
`
`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
})
`

`typescript
addPropToObjects<
T extends object,
K extends string,
R
>(
property: K,
fn: (input: T) => R
): (list: T[]) => MergeTypes
`
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'}]
Try this R.addPropToObjects example in Rambda REPL
`typescript`
addPropToObjects<
T extends object,
K extends string,
R
>(
property: K,
fn: (input: T) => R
): (list: T[]) => MergeTypes
`javascript
import { mapFn } from './map.js'
export function addPropToObjects (
property,
fn
){
return listOfObjects => mapFn(
(obj) => ({
...(obj),
[property]: fn(obj)
}),
listOfObjects
)
}
`
`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' },
])
})
`
`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; }[]
})
`

`typescript
all
`
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
`
Try this R.all example in Rambda REPL
`typescript`
all
`javascript
export function all(predicate) {
return list => {
for (let i = 0; i < list.length; i++) {
if (!predicate(list[i])) {
return false
}
}
return true
}
}
`
`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()
})
`
`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
})
})
`

`typescript
allPass
`
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]]
Try this R.allPass example in Rambda REPL
`typescript`
allPass
`javascript
export function allPass(predicates) {
return input => {
let counter = 0
while (counter < predicates.length) {
if (!predicatescounter) {
return false
}
counter++
}
return true
}
}
`
`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([])
})
`
`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[]
})
})
`

`typescript
any
`
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
Try this R.any example in Rambda REPL
`typescript`
any
`javascript
export function any(predicate) {
return list => {
let counter = 0
while (counter < list.length) {
if (predicate(list[counter], counter)) {
return true
}
counter++
}
return false
}
}
`
`javascript
import { any } from './any.js'
const list = [1, 2, 3]
test('happy', () => {
expect(any(x => x > 2)(list)).toBeTruthy()
})
`
`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
})
`

`typescript
anyPass
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
`
Try this R.anyPass example in Rambda REPL
`typescript`
anyPass
predicates: [(a: T) => a is TF1, (a: T) => a is TF2],
): (a: T) => a is TF1 | TF2;
anyPass
predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3],
): (a: T) => a is TF1 | TF2 | TF3;
anyPass
predicates: [(a: T) => a is TF1, (a: T) => a is TF2, (a: T) => a is TF3],
): (a: T) => a is TF1 | TF2 | TF3;
anyPass
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;
...
...
`javascript
export function anyPass(predicates) {
return input => {
let counter = 0
while (counter < predicates.length) {
if (predicatescounter) {
return true
}
counter++
}
return false
}
}
`
`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()
})
`
`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
}
})
})
`

`typescript
append
`
It adds element x at the end of iterable.
`javascript`
const result = R.append('foo')(['bar', 'baz'])
// => ['bar', 'baz', 'foo']
Try this R.append example in Rambda REPL
`typescript`
append
append
`javascript
import { cloneList } from './_internals/cloneList.js'
export function append(x) {
return list => {
const clone = cloneList(list)
clone.push(x)
return clone
}
}
`
`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'])
})
`
`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; }[]
})
})
`

`typescript
ascend
`
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}]
Try this R.ascend example in Rambda REPL
`typescript`
ascend
`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)
}
}
`
`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}])
})
`
`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; }[]
})
`

`typescript
assertType
`
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.
`typescript`
assertType
`javascript`
export function assertType(fn) {
return (x) => {
if (fn(x)) {
return x
}
throw new Error('type assertion failed in R.assertType')
}
}
`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')
})
`
`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
})
`

`typescript
checkObjectWithSpec
`
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
`
Try this R.checkObjectWithSpec example in Rambda REPL
`typescript`
checkObjectWithSpec
`javascript
export function checkObjectWithSpec(conditions) {
return input => {
let shouldProceed = true
for (const prop in conditions) {
if (!shouldProceed) {
continue
}
const result = conditionsprop
if (shouldProceed && result === false) {
shouldProceed = false
}
}
return shouldProceed
}
}
`
`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)
})
`
`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
})
})
`

`typescript
compact
`
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 } }
Try this R.compact example in Rambda REPL
`typescript`
compact
compact
[K in keyof T as Exclude
? never
: K
]: Exclude
};
`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)
}
`
`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 })
})
`
`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; }
})
`

`typescript
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).
`javascript
const fn = x => x > 5
const inverted = complement(fn)
const result = [
fn(7),
inverted(7)
] => [ true, false ]
`
Try this R.complement example in Rambda REPL
`typescript`
complement
`javascript`
export function complement(fn) {
return (...input) => !fn(...input)
}
`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()
})
`
`typescript
import { complement } from 'rambda'
describe('R.complement', () => {
it('happy', () => {
const fn = complement((x: number) => x > 10)
const result = fn(1)
result // $ExpectType boolean
})
})
`

`typescript
concat
`
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'
Try this R.concat example in Rambda REPL
`typescript`
concat
concat(x: string): (y: string) => string;
`javascript${x}${y}
export function concat(x) {
return y => (typeof x === 'string' ? : [...x, ...y])`
}
`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
})
`

`typescript
convertToType
`
It helps to convert a value to a specific type.
It is useful when you have to overcome TypeScript's type inference.
`typescript`
convertToType
`javascript`
export function convertToType(x) {
return x
}
`typescript
import { convertToType, pipe } from 'rambda'
const list = [1, 2, 3]
it('R.convertToType', () => {
const result = pipe(list,
convertToType
x => {
x // $ExpectType string[]
return x
}
)
result // $ExpectType string[]
})
`

`typescript
count
`
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
Try this R.count example in Rambda REPL
`typescript`
count
`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
}
}
`
`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)
})
`
`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
})
`

`typescript
countBy
`
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`
Try this R.countBy example in Rambda REPL
`typescript`
countBy
`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
}
}
`
`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
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; }
})
`

`typescript
createObjectFromKeys
fn: (key: K[number]) => V
): (keys: K) => { [P in K[number]]: V }
`
`javascript${x}-${index}
const result = R.createObjectFromKeys(
(x, index) => `
)(['a', 'b', 'c'])
// => {a: 'a-0', b: 'b-1', c: 'c-2'}
Try this R.createObjectFromKeys example in Rambda REPL
`typescript`
createObjectFromKeys
fn: (key: K[number]) => V
): (keys: K) => { [P in K[number]]: V };
createObjectFromKeys
fn: (key: K[number], index: number) => V
): (keys: K) => { [P in K[number]]: V };
`javascript
export function createObjectFromKeys(fn) {
return keys => {
const result = {}
keys.forEach((key, index) => {
result[key] = fn(key, index)
})
return result
}
}
`
`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)
})
`

`typescript
defaultTo
`
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'
`
Try this R.defaultTo example in Rambda REPL
`typescript`
defaultTo
`javascript
function isFalsy(input) {
return input === undefined || input === null || Number.isNaN(input) === true
}
export function defaultTo(defaultArgument) {
return input => isFalsy(input) ? defaultArgument : input
}
`
`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')
})
`
`typescript
import { defaultTo, pipe } from 'rambda'
describe('R.defaultTo', () => {
it('happy', () => {
const result = pipe('bar' as unknown, defaultTo('foo'))
result // $ExpectType string
})
})
`

`typescript
delay(ms: number): Promise<'RAMBDA_DELAY'>
`
setTimeout as a promise that resolves to RAMBDA_DELAY string after ms milliseconds.
`typescript`
delay(ms: number): Promise<'RAMBDA_DELAY'>;
`javascript
export const RAMBDA_DELAY = 'RAMBDA_DELAY'
export function delay(ms) {
return new Promise(resolve => {
setTimeout(() => {
resolve(RAMBDA_DELAY)
}, ms)
})
}
`

`typescript
descend
`
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}]
Try this R.descend example in Rambda REPL
`typescript`
descend
`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)
}
}
`

`typescript
difference
`
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 ]
`
Try this R.difference example in Rambda REPL
`typescript`
difference
`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),
])
}
`
`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 },
])
})
`
`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; }[]
})
})
`

`typescript
drop
`
It returns howMany items dropped from beginning of list.
`javascript`
R.drop(2)(['foo', 'bar', 'baz']) // => ['baz']
Try this R.drop example in Rambda REPL
`typescript`
drop
`javascript`
export function drop(howManyToDrop) {
return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
`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])
})
`
`typescript
import { drop, pipe } from 'rambda'
it('R.drop', () => {
const result = pipe([1, 2, 3, 4], drop(2))
result // $ExpectType number[]
})
`

`typescript
dropLast
`
It returns howMany items dropped from the end of list.
`typescript`
dropLast
`javascript`
export function dropLast(numberItems) {
return list => (numberItems > 0 ? list.slice(0, -numberItems) : list.slice())
}
`javascript
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 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(Number.NEGATIVE_INFINITY)([1, 2, 3])).toEqual([1, 2, 3])
})
`

`typescript
dropLastWhile
`
`javascript
const list = [1, 2, 3, 4, 5];
const predicate = x => x >= 3
const result = dropLastWhile(predicate)(list);
// => [1, 2]
`
Try this R.dropLastWhile example in Rambda REPL
`typescript`
dropLastWhile
dropLastWhile
`javascript
export function dropLastWhile(predicate) {
return list => {
if (list.length === 0) {
return list
}
const toReturn = []
let counter = list.length
while (counter) {
const item = list[--counter]
if (!predicate(item, counter)) {
toReturn.push(item)
break
}
}
while (counter) {
toReturn.push(list[--counter])
}
return toReturn.reverse()
}
}
`
`javascript
import { dropLastWhile } from './dropLastWhile.js'
const list = [1, 2, 3, 4, 5]
test('with list', () => {
const result = dropLastWhile(x => x >= 3)(list)
expect(result).toEqual([1, 2])
})
test('with empty list', () => {
expect(dropLastWhile(() => true)([])).toEqual([])
})
`

`typescript
dropRepeatsBy
`
`javascript`
const result = R.dropRepeatsBy(
Math.abs,
[1, -1, 2, 3, -3]
)
// => [1, 2, 3]
Try this R.dropRepeatsBy example in Rambda REPL
`typescript`
dropRepeatsBy

`typescript
dropRepeatsWith
`
`javascript
const list = [{a:1,b:2}, {a:1,b:3}, {a:2, b:4}]
const result = R.dropRepeatsWith(R.prop('a'))(list)
// => [{a:1,b:2}, {a:2, b:4}]
`
Try this R.dropRepeatsWith example in Rambda REPL
`typescript`
dropRepeatsWith

`typescript
dropWhile
`
`javascript`
const list = [1, 2, 3, 4]
const predicate = x => x < 3
const result = R.dropWhile(predicate)(list)
// => [3, 4]
Try this R.dropWhile example in Rambda REPL
`typescript`
dropWhile
dropWhile
`javascript
export function dropWhile(predicate) {
return iterable => {
const toReturn = []
let counter = 0
while (counter < iterable.length) {
const item = iterable[counter++]
if (!predicate(item, counter)) {
toReturn.push(item)
break
}
}
while (counter < iterable.length) {
toReturn.push(iterable[counter++])
}
return toReturn
}
}
`
`javascript
import { dropWhile } from './dropWhile.js'
const list = [1, 2, 3, 4]
test('happy', () => {
const predicate = (x, i) => {
expect(typeof i).toBe('number')
return x < 3
}
const result = dropWhile(predicate)(list)
expect(result).toEqual([3, 4])
})
test('always false', () => {
const predicate = () => 0
const result = dropWhile(predicate)(list)
expect(result).toEqual(list)
})
`
`typescript
import { dropWhile, pipe } from 'rambda'
const list = [1, 2, 3]
describe('R.dropWhile', () => {
it('happy', () => {
const result = pipe(
list,
dropWhile(x => x > 1),
)
result // $ExpectType number[]
})
it('with index', () => {
const result = pipe(
list,
dropWhile((x, i) => {
i // $ExpectType number
return x + i > 2
}),
)
result // $ExpectType number[]
})
})
`

`typescript
duplicateBy
`
`javascript
const list = [{a:1}, {a:2}, {a:1}]
const result = R.duplicateBy(x => x, list)
// => [{a:1}]
`
Try this R.duplicateBy example in Rambda REPL
`typescript`
duplicateBy
`javascript
import { _Set } from '../src/_internals/set.js'
export function duplicateBy(fn) {
return list => {
const set = new _Set()
return list.filter(item => !set.checkUniqueness(fn(item)))
}
}
`
`javascript
import { duplicateBy } from './duplicateBy.js'
test('happy', () => {
expect(duplicateBy(Math.abs)([-2, -1, 0, 1, 2])).toEqual([1,2])
})
test('returns an empty array for an empty array', () => {
expect(duplicateBy(Math.abs)([])).toEqual([])
})
test('uses R.uniq', () => {
const list = [{ a: 1 }, { a: 2 }, { a: 1 }]
const expected = [{ a: 1 }]
expect(duplicateBy(x => x)(list)).toEqual(expected)
})
`

`typescript
eqBy
`
`javascript`
const result = R.eqBy(Math.abs, 5)(-5)
// => true
Try this R.eqBy example in Rambda REPL
`typescript`
eqBy
`javascript
import { equalsFn } from './equals.js'
export function eqBy(fn, a) {
return b => equalsFn(fn(a), fn(b))
}
`
`javascript
import { eqBy } from './eqBy.js'
test('deteremines whether two values map to the same value in the codomain', () => {
expect(eqBy(Math.abs, 5)(5)).toBe(true)
expect(eqBy(Math.abs, 5)(-5)).toBe(true)
expect(eqBy(Math.abs, -5)(5)).toBe(true)
expect(eqBy(Math.abs, -5)(-5)).toBe(true)
expect(eqBy(Math.abs, 42)(99)).toBe(false)
})
test('has R.equals semantics', () => {
expect(eqBy(Math.abs, Number.NaN)(Number.NaN)).toBe(true)
expect(eqBy(Math.abs, [42])([42])).toBe(true)
expect(eqBy(x => x, { a: 1 })({ a: 1 })).toBe(true)
expect(eqBy(x => x, { a: 1 })({ a: 2 })).toBe(false)
})
`

`typescript
eqProps
`
It returns true if property prop in obj1 is equal to property prop in obj2 according to R.equals.
`javascript`
const obj1 = {a: 1, b:2}
const obj2 = {a: 1, b:3}
const result = R.eqProps('a', obj1)(obj2)
// => true
Try this R.eqProps example in Rambda REPL
`typescript`
eqProps
`javascript
import { equalsFn } from './equals.js'
export function eqProps(property, objA) {
return objB => equalsFn( objA[property], objB[property] )
}
`
`javascript
import { eqProps } from './eqProps.js'
const obj1 = {
a: 1,
b: 2,
}
const obj2 = {
a: 1,
b: 3,
}
test('props are equal', () => {
const result = eqProps('a', obj1)(obj2)
expect(result).toBeTruthy()
})
test('props are not equal', () => {
const result = eqProps('b', obj1)(obj2)
expect(result).toBeFalsy()
})
test('prop does not exist', () => {
const result = eqProps('c', obj1)(obj2)
expect(result).toBeTruthy()
})
`
`typescript
import { eqProps, pipe } from 'rambda'
const obj1 = { a: { b: 1 }, c: 2 }
const obj2 = { a: { b: 1 }, c: 3 }
it('R.eqProps', () => {
const result = pipe(obj1, eqProps('a', obj2))
result // $ExpectType boolean
})
`

`typescript
equals
`
It deeply compares x and y and returns true if they are equal.
> :boom: It doesn't handle cyclical data structures and functions
`javascript`
R.equals(
[1, {a:2}, [{b: 3}]],
[1, {a:2}, [{b: 3}]]
) // => true
Try this R.equals example in Rambda REPL
`typescript`
equals
equals
`javascript
import { isArray } from './_internals/isArray.js'
import { type } from './type.js'
export function _lastIndexOf(valueToFind, list) {
if (!isArray(list)) {
throw new Error(Cannot read property 'indexOf' of ${list})
}
const typeOfValue = type(valueToFind)
if (!['Array', 'NaN', 'Object', 'RegExp'].includes(typeOfValue)) {
return list.lastIndexOf(valueToFind)
}
const { length } = list
let index = length
let foundIndex = -1
while (--index > -1 && foundIndex === -1) {
if (equalsFn(list[index], valueToFind)) {
foundIndex = index
}
}
return foundIndex
}
export function _indexOf(valueToFind, list) {
if (!isArray(list)) {
throw new Error(Cannot read property 'indexOf' of ${list}`)
}
const typeOfValue = type(valueToFind)
if (!['Array', 'NaN', 'Object', 'RegExp'].includes(typeOfValue)) {
return list.indexOf(valueToFind)
}
let index = -1
let foundIndex = -1
const { length } = list
while (++index < length && foundIndex === -1) {
if (equalsFn(list[index], valueToFind)) {
foundIndex = index
}
}
return foundIndex
}
function _arrayFromIterator(iter) {
const list = []
let next
while (!(next = iter.next()).done) {
list.push(next.value)
}
return list
}
function _compareSets(a, b) {
if (a.size !== b.size) {
return false
}
const aList = _arrayFromIterator(a.values())
const bList = _arrayFromIterator(b.values())
const filtered = aList.filter(aInstance => _indexOf(aInstance, bList) === -1)
return filtered.length === 0
}
function compareErrors(a, b) {
if (a.message !== b.message) {
return false
}
if (a.toString !== b.toString) {
return false
}
return a.toS