Miss any of our Open RFC calls?Watch the recordings here! »

rambdax

7.0.1 • Public • Published

Rambdax

Extended version of Rambda(utility library) - Documentation

Rambda is smaller and faster alternative to the popular functional programming library Ramda. - Documentation

CircleCI codecov dependencies Status Library size

➤ Differences between Rambda and Rambdax

Rambdax passthrough all Rambda methods and introduce some new functions.

The idea of Rambdax is to extend Rambda without worring for Ramda compatibility.

---------------

➤ Example use

import { composeAsync, filter, delay, mapAsync } from 'rambdax'
 
const result = await composeAsync(
  mapAsync(async x => {
    await delay(100)
    return x + 1
  }),
  filter(x => x > 1)
)([1, 2, 3])
// => [3, 4]

You can test this example in Rambda's REPL

---------------

➤ Rambdax's advantages

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

Extendable with Ramda community projects

Rambdax implements some methods from Ramda community projects, such as R.lensSatisfies, R.lensEq and R.viewOr.

Support

Most of the valid issues are fixed within 2-3 days.

Closing the issue is usually accompanied by publishing a new patch version of Rambdax to NPM.

---------------

➤ Missing Ramda methods

Click to see the full list of 90 Ramda methods not implemented in Rambda
  • __
  • addIndex
  • ap
  • aperture
  • apply
  • applyTo
  • ascend
  • binary
  • bind
  • call
  • comparator
  • composeK
  • composeP
  • composeWith
  • construct
  • constructN
  • contains
  • countBy
  • descend
  • differenceWith
  • dissocPath
  • empty
  • eqBy
  • forEachObjIndexed
  • gt
  • gte
  • hasIn
  • innerJoin
  • insert
  • insertAll
  • into
  • invert
  • invertObj
  • invoker
  • juxt
  • keysIn
  • lift
  • liftN
  • lt
  • lte
  • mapAccum
  • mapAccumRight
  • mapObjIndexed
  • memoizeWith
  • mergeDeepLeft
  • mergeDeepWith
  • mergeDeepWithKey
  • mergeRight
  • mergeWith
  • mergeWithKey
  • nAry
  • nthArg
  • o
  • objOf
  • otherwise
  • pair
  • partialRight
  • pathSatisfies
  • pickBy
  • pipeK
  • pipeP
  • pipeWith
  • project
  • propSatisfies
  • reduceBy
  • reduceRight
  • reduceWhile
  • reduced
  • remove
  • scan
  • sequence
  • sortWith
  • symmetricDifferenceWith
  • andThen
  • toPairsIn
  • transduce
  • traverse
  • unapply
  • unary
  • uncurryN
  • unfold
  • unionWith
  • uniqBy
  • unnest
  • until
  • useWith
  • valuesIn
  • xprod
  • thunkify
  • default

---------------

➤ Install

  • yarn add rambdax

  • For UMD usage either use ./dist/rambdax.umd.js or the following CDN link:

https://unpkg.com/rambdax@CURRENT_VERSION/dist/rambdax.umd.js
  • with deno
import {compose, add} from 'https://raw.githubusercontent.com/selfrefactor/rambdax/master/dist/rambdax.esm.js'

---------------

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.

  • 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. In case of the former, you may want to consult with Rambda contribution guidelines.

---------------

➤ 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.19) and Ramda(0.27.0).

method Rambda Ramda Lodash
add 96.25% slower 96.24% slower 🚀 Fastest
adjust 🚀 Fastest 5.52% slower 🔳
all 🚀 Fastest 94.95% slower 🔳
allPass 🚀 Fastest 98.95% slower 🔳
any 🚀 Fastest 98.18% slower 6.18% slower
anyPass 🚀 Fastest 99.09% slower 🔳
append 🚀 Fastest 84.09% slower 🔳
applySpec 🚀 Fastest 75.73% slower 🔳
assoc 87.98% slower 57.39% slower 🚀 Fastest
clone 🚀 Fastest 96.03% slower 91.75% slower
compose 🚀 Fastest 96.45% slower 77.83% slower
converge 49.12% slower 🚀 Fastest 🔳
curry 🚀 Fastest 34.9% slower 🔳
curryN 63.32% slower 🚀 Fastest 🔳
defaultTo 🚀 Fastest 50.3% slower 🔳
drop 🚀 Fastest 97.45% slower 🔳
dropLast 🚀 Fastest 97.07% slower 🔳
equals 72.11% slower 79.48% slower 🚀 Fastest
filter 🚀 Fastest 94.74% slower 58.18% slower
find 🚀 Fastest 98.2% slower 88.96% slower
findIndex 🚀 Fastest 97.97% slower 79.39% slower
flatten 6.56% slower 95.38% slower 🚀 Fastest
ifElse 🚀 Fastest 70.97% slower 🔳
includes 🚀 Fastest 71.7% slower 🔳
indexOf 🚀 Fastest 84.08% slower 7.86% slower
init 94.42% slower 97.55% slower 🚀 Fastest
is 🚀 Fastest 11.72% slower 🔳
isEmpty 51.68% slower 93.82% slower 🚀 Fastest
last 🚀 Fastest 99.64% slower 1.05% slower
lastIndexOf 🚀 Fastest 42.38% slower 🔳
map 🚀 Fastest 69.63% slower 4.68% slower
match 🚀 Fastest 46.75% slower 🔳
merge 63.55% slower 🚀 Fastest 55.25% slower
none 🚀 Fastest 98.22% slower 🔳
omit 🚀 Fastest 70.66% slower 97.56% slower
over 🚀 Fastest 50.77% slower 🔳
path 🚀 Fastest 74.94% slower 5.72% slower
pick 🚀 Fastest 26.29% slower 86.82% slower
prop 🚀 Fastest 89.89% slower 🔳
propEq 🚀 Fastest 95.26% slower 🔳
range 95.17% slower 90.22% slower 🚀 Fastest
reduce 52.76% slower 74.02% slower 🚀 Fastest
repeat 85.91% slower 95.31% slower 🚀 Fastest
replace 0.47% slower 28.13% slower 🚀 Fastest
set 🚀 Fastest 36.26% slower 🔳
sort 🚀 Fastest 63.15% slower 🔳
sortBy 🚀 Fastest 61.57% slower 88.88% slower
split 🚀 Fastest 85.34% slower 33.69% slower
splitEvery 🚀 Fastest 90.18% slower 🔳
take 93.44% slower 98.04% slower 🚀 Fastest
takeLast 92.61% slower 98.83% slower 🚀 Fastest
test 🚀 Fastest 94.42% slower 🔳
type 18.91% slower 🚀 Fastest 🔳
uniq 98.98% slower 96.58% slower 🚀 Fastest
update 🚀 Fastest 38.88% slower 🔳
view 🚀 Fastest 82.21% slower 🔳

---------------

➤ Used by

---------------

API

add

 
add(anumber, bnumber)number

It adds a and b.

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

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

Try this R.add example in Rambda REPL

All Typescript definitions
add(anumber, bnumber)number;
add(anumber): (b: number) => number;
Tests
import { add } from './add'
 
test('with number', () => {
  expect(add(2, 3)).toEqual(5)
  expect(add(7)(10)).toEqual(17)
})
 
test('string is bad input', () => {
  expect(add('foo', 'bar')).toBeNaN()
})
 
test('ramda specs', () => {
  expect(add('1', '2')).toEqual(3)
  expect(add(1, '2')).toEqual(3)
  expect(add(true, false)).toEqual(1)
  expect(add(null, null)).toEqual(0)
  expect(add(undefined, undefined)).toEqual(NaN)
  expect(add(new Date(1), new Date(2))).toEqual(3)
})

---------------

adjust

 
adjust<T>(indexnumber, replaceFn: (x: T) => T, listT[])T[]

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

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

Try this R.adjust example in Rambda REPL

All Typescript definitions
adjust<T>(indexnumber, replaceFn: (x: T) => T, listT[])T[];
adjust<T>(indexnumber, replaceFn: (x: T) => T): (list: T[]) => T[];
Tests
import { add } from './add'
import { adjust } from './adjust'
import { pipe } from './pipe'
 
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)
})

---------------

all

 
all<T>(predicate: (x: T) => boolean, listT[])boolean

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

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
all<T>(predicate: (x: T) => boolean, listT[])boolean;
all<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
Tests
import { all } from './all'
 
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()
})

---------------

allFalse

 
allFalse(...inputsany[])boolean

It returns true if all inputs arguments are falsy(empty objects and empty arrays are considered falsy).

Functions are valid inputs, but these functions cannot have their own arguments.

This method is very similar to R.anyFalse, R.anyTrue and R.allTrue

R.allFalse(0, null, [], {}, '', () => false)
// => true

Try this R.allFalse example in Rambda REPL

All Typescript definitions
allFalse(...inputsany[])boolean;
Tests
import { runTests } from 'helpers-fn'
 
import { allFalse } from './allFalse'
 
const happy = { ok : [ () => false, () => [], () => {}, null, false, [] ] }
const withArray = { fail : [ ...happy.ok, [ 1 ] ] }
const withObject = { fail : [ ...happy.ok, { a : 1 } ] }
const withFunction = { fail : [ ...happy.ok, () => ({ a : 1 }) ] }
const withBoolean = { fail : [ ...happy.ok, true ] }
 
const testData = {
  label : 'R.allFalse',
  data  : [ happy, withArray, withObject, withFunction, withBoolean ],
  fn    : input => allFalse(...input),
}
runTests(testData)

---------------

allPass

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

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

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
allPass<T>(predicates((x: T) => boolean)[]): (input: T) => boolean;
Tests
import { allPass } from './allPass'
 
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()
})

---------------

allTrue

 
allTrue(...inputany[])boolean

It returns true if all inputs arguments are truthy(empty objects and empty arrays are considered falsy).

R.allTrue(1, true, {a: 1}, [1], 'foo', () => true)
// => true

Try this R.allTrue example in Rambda REPL

All Typescript definitions
allTrue(...inputany[])boolean;
Tests
import { allTrue } from './allTrue'
 
test('with functions', () => {
  const foo = () => 1
  const bar = () => false
  const baz = () => JSON.parse('{sda')
  const result = allTrue(
    foo, bar, baz
  )
  expect(result).toBeFalse()
})
 
test('usage with non boolean', () => {
  const foo = { a : 1 }
  const baz = [ 1, 2, 3 ]
 
  const result = allTrue(
    foo, foo, baz
  )
  expect(result).toBeTrue()
})
 
test('usage with boolean', () => {
  const foo = 4
  const baz = [ 1, 2, 3 ]
 
  const result = allTrue(foo > 2, baz.length === 3)
  expect(result).toBeTrue()
})
 
test('escapes early - case 0', () => {
  const foo = undefined
  const result = allTrue(foo, () => foo.a)
  expect(result).toBeFalse()
})
 
test('escapes early - case 1', () => {
  const foo = null
  const result = allTrue(foo, () => foo.a)
  expect(result).toBeFalse()
})
 
test('escapes early - case 2', () => {
  const foo = { a : 'bar' }
  const result = allTrue(
    foo, foo.a, foo.a.b
  )
  expect(result).toBeFalse()
})
 
test('escapes early - case 3', () => {
  const foo = { a : { b : 'foo' } }
  const result = allTrue(
    foo,
    () => foo.a,
    () => foo.a.b
  )
  expect(result).toBeTrue()
})

---------------

allType

 
allType(targetTypeRambdaTypes): (...input: any[]) => boolean

It returns a function which will return true if all of its inputs arguments belong to targetType.

💥 targetType is one of the possible returns of R.type

const targetType = 'String'
 
const result = R.allType(
  targetType
)('foo', 'bar', 'baz')
// => true

Try this R.allType example in Rambda REPL

All Typescript definitions
allType(targetTypeRambdaTypes): (...input: any[]) => boolean;
Tests
import { allType } from './allType'
 
test('when true', () => {
  const result = allType('Array')(
    [ 1, 2, 3 ], [], [ null ]
  )
 
  expect(result).toBeTrue()
})
 
test('when false', () => {
  const result = allType('String')(
    1, undefined, null, []
  )
 
  expect(result).toBeFalse()
})

---------------

always

 
always<T>(xT): () => T

It returns function that always returns x.

const fn = R.always(7)
 
console.log(fn())// => 7

Try this R.always example in Rambda REPL

All Typescript definitions
always<T>(xT): () => T;
Tests
import { always } from './always'
import { F } from './F'
 
test('happy', () => {
  const fn = always(7)
 
  expect(fn()).toEqual(7)
  expect(fn()).toEqual(7)
})
 
test('f', () => {
  const fn = always(F())
 
  expect(fn()).toBeFalse()
  expect(fn()).toBeFalse()
})

---------------

and

 
and<T, U>(xT, yU)T | U

Logical AND

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

Try this R.and example in Rambda REPL

All Typescript definitions
and<T, U>(xT, yU)T | U;
and<T>(xT): <U>(y: U) => T | U;
Tests
import { and } from './and'
 
test('happy', () => {
  expect(and(1, 'foo')).toBe('foo')
  expect(and(true, true)).toBeTrue()
  expect(and(true)(true)).toBeTrue()
  expect(and(true, false)).toBeFalse()
  expect(and(false, true)).toBeFalse()
  expect(and(false, false)).toBeFalse()
})

---------------

any

 
any<T>(predicate: (x: T) => boolean, listreadonly T[])boolean

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

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
any<T>(predicate: (x: T) => boolean, listreadonly T[])boolean;
any<T>(predicate: (x: T) => boolean): (list: readonly T[]) => boolean;
Tests
import { any } from './any'
 
const list = [ 1, 2, 3 ]
 
test('happy', () => {
  expect(any(x => x < 0, list)).toBeFalse()
})
 
test('with curry', () => {
  expect(any(x => x > 2)(list)).toBeTrue()
})

---------------

anyFalse

 
anyFalse(...inputany[])boolean

It returns true if any of inputs is falsy(empty objects and empty arrays are considered falsy).

R.anyFalse(1, {a: 1}, [1], () => false)
// => true

Try this R.anyFalse example in Rambda REPL

All Typescript definitions
anyFalse(...inputany[])boolean;
Tests
import { anyFalse } from './anyFalse'
 
test('when true', () => {
  expect(anyFalse(
    true, true, false
  )).toBeTruthy()
})
 
test('when false', () => {
  expect(anyFalse(true, true)).toBeFalsy()
})
 
test('supports function', () => {
  expect(anyFalse(
    true,
    () => true,
    () => false
  )).toBeTruthy()
})

---------------

anyPass

 
anyPass<T>(predicatesSafePred<T>[])SafePred<T>

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.

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
anyPass<T>(predicatesSafePred<T>[])SafePred<T>;
Tests
import { anyPass } from './anyPass'
 
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('happy', () => {
  expect(anyPass([])(3)).toEqual(false)
})

---------------

anyTrue

 
anyTrue(...inputany[])boolean

It returns true if any of inputs arguments are truthy(empty objects and empty arrays are considered falsy).

R.anyTrue(0, null, [], {}, '', () => true)
// => true

Try this R.anyTrue example in Rambda REPL

All Typescript definitions
anyTrue(...inputany[])boolean;
Tests
import { anyTrue } from './anyTrue'
 
test('when true', () => {
  expect(anyTrue(
    true, true, false
  )).toBeTruthy()
})
 
test('when false', () => {
  expect(anyTrue(
    false, false, false
  )).toBeFalsy()
})
 
test('supports function', () => {
  expect(anyTrue(
    false,
    false,
    false,
    () => false,
    () => true
  )).toBeTruthy()
})

---------------

anyType

 
anyType(targetTypeRambdaTypes): (...input: any[]) => boolean

It returns a function which will return true if at least one of its inputs arguments belongs to targetType.

targetType is one of the possible returns of R.type

💥 targetType is one of the possible returns of R.type

const targetType = 'String'
 
const result = R.anyType(
  targetType
)(1, {}, 'foo')
// => true

Try this R.anyType example in Rambda REPL

All Typescript definitions
anyType(targetTypeRambdaTypes): (...input: any[]) => boolean;
Tests
import { anyType } from './anyType'
 
test('when true', () => {
  const result = anyType('Array')(
    1, undefined, null, []
  )
 
  expect(result).toBeTrue()
})
 
test('when false', () => {
  const result = anyType('String')(
    1, undefined, null, []
  )
 
  expect(result).toBeFalse()
})

---------------

append

 
append<T>(xT, listreadonly T[])T[]

It adds element x at the end of list.

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

Try this R.append example in Rambda REPL

All Typescript definitions
append<T>(xT, listreadonly T[])T[];
append<T>(xT): <T>(list: readonly T[]) => T[];
Tests
import { append } from './append'
 
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' ])
})

---------------

applyDiff

 
applyDiff<Output>(rulesApplyDiffRule[], objobject)Output

It changes paths in an object according to a list of operations. Valid operations are add, update and delete. Its use-case is while writing tests and you need to change the test data.

Note, that you cannot use update operation, if the object path is missing in the input object. Also, you cannot use add operation, if the object path has a value.

const obj = {a: {b:1, c:2}}
const rules = [
  {op: 'remove', path: 'a.c'},
  {op: 'add', path: 'a.d', value: 4},
  {op: 'update', path: 'a.b', value: 2},
]
const result = R.applyDiff(rules, obj)
const expected = {a: {b: 2, d: 4}}
 
// => `result` is equal to `expected`

Try this R.applyDiff example in Rambda REPL

All Typescript definitions
applyDiff<Output>(rulesApplyDiffRule[], objobject)Output;
applyDiff<Output>(rulesApplyDiffRule[]): ( obj: object) => Output;
Tests
import { applyDiff } from './applyDiff'
 
test('remove operation', () => {
  const rules = [
    {
      op   : 'remove',
      path : 'a.b',
    },
  ]
  const result = applyDiff(rules, {
    a : {
      b : 1,
      c : 2,
    },
  })
  expect(result).toEqual({ a : { c : 2 } })
})
 
test('update operation', () => {
  const rules = [
    {
      op    : 'update',
      path  : 'a.b',
      value : 3,
    },
    {
      op    : 'update',
      path  : 'a.c.1',
      value : 3,
    },
    {
      op    : 'update',
      path  : 'a.d',
      value : 3,
    },
  ]
  const result = applyDiff(rules, {
    a : {
      b : 1,
      c : [ 1, 2 ],
    },
  })
  expect(result).toEqual({
    a : {
      b : 3,
      c : [ 1, 3 ],
    },
  })
})
 
test('add operation', () => {
  const rules = [
    {
      op    : 'add',
      path  : 'a.b',
      value : 3,
    },
    {
      op    : 'add',
      path  : 'a.d',
      value : 3,
    },
  ]
  const result = applyDiff(rules, {
    a : {
      b : 1,
      c : 2,
    },
  })
 
  expect(result).toEqual({
    a : {
      b : 1,
      c : 2,
      d : 3,
    },
  })
})

---------------

applySpec

 
applySpec<Spec extends Record<string, (...args: readonly any[]) => any>>(
  spec: Spec
): (
  ...argsParameters<ValueOfRecord<Spec>>
) => { [Key in keyof Spec]ReturnType<Spec[Key]> }

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

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
applySpec<Spec extends Record<string, (...args: readonly any[]) => any>>(
  spec: Spec
): (
  ...argsParameters<ValueOfRecord<Spec>>
) => { [Key in keyof Spec]ReturnType<Spec[Key]> };
applySpec<T>(specany): (...args: readonly any[]) => T;
Tests
import { applySpec as applySpecRamda, nAry } from 'ramda'
 
import { add, always, compose, dec, inc, map, path, prop, T } from '../rambda'
import { applySpec } from './applySpec'
 
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.length).toBe(0)
  expect(fRamda.length).toBe(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',
  })
})

---------------

assoc

 
assoc<T, U, K extends string>(propK, valT, objU)Record<K, T> & U

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

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

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

Try this R.assoc example in Rambda REPL

All Typescript definitions
assoc<T, U, K extends string>(propK, valT, objU)Record<K, T> & U;
assoc<T, K extends string>(propK, valT): <U>(obj: U) => Record<K, T> & U;
assoc<K extends string>(propK)AssocPartialOne<K>;
Tests
import { assoc } from './assoc'
 
test('adds a key to an empty object', () => {
  expect(assoc(
    'a', 1, {}
  )).toEqual({ a : 1 })
})
 
test('adds a key to a non-empty object', () => {
  expect(assoc(
    'b', 2, { a : 1 }
  )).toEqual({
    a : 1,
    b : 2,
  })
})
 
test('adds a key to a non-empty object - curry case 1', () => {
  expect(assoc('b', 2)({ a : 1 })).toEqual({
    a : 1,
    b : 2,
  })
})
 
test('adds a key to a non-empty object - curry case 2', () => {
  expect(assoc('b')(2, { a : 1 })).toEqual({
    a : 1,
    b : 2,
  })
})
 
test('adds a key to a non-empty object - curry case 3', () => {
  const result = assoc('b')(2)({ a : 1 })
 
  expect(result).toEqual({
    a : 1,
    b : 2,
  })
})
 
test('changes an existing key', () => {
  expect(assoc(
    'a', 2, { a : 1 }
  )).toEqual({ a : 2 })
})
 
test('undefined is considered an empty object', () => {
  expect(assoc(
    'a', 1, undefined
  )).toEqual({ a : 1 })
})
 
test('null is considered an empty object', () => {
  expect(assoc(
    'a', 1, null
  )).toEqual({ a : 1 })
})
 
test('value can be null', () => {
  expect(assoc(
    'a', null, null
  )).toEqual({ a : null })
})
 
test('value can be undefined', () => {
  expect(assoc(
    'a', undefined, null
  )).toEqual({ a : undefined })
})
 
test('assignment is shallow', () => {
  expect(assoc(
    'a', { b : 2 }, { a : { c : 3 } }
  )).toEqual({ a : { b : 2 } })
})

---------------

assocPath

 
assocPath<Output>(pathPath, newValueany, objobject)Output

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

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

Try this R.assocPath example in Rambda REPL

All Typescript definitions
assocPath<Output>(pathPath, newValueany, objobject)Output;
assocPath<Output>(pathPath, newValueany): (obj: object) => Output;
assocPath<Output>(pathPath)FunctionToolbelt.Curry<(newValue: any, obj: object) => Output>;
Tests
import { assocPath } from './assocPath'
 
test('string can be used as path input', () => {
  const testObj = {
    a : [ { b : 1 }, { b : 2 } ],
    d : 3,
  }
  const result = assocPath(
    'a.0.b', 10, testObj
  )
  const expected = {
    a : [ { b : 10 }, { b : 2 } ],
    d : 3,
  }
  expect(result).toEqual(expected)
})
 
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).toEqual(3)
})
 
test('happy', () => {
  const expected = { foo : { bar : { baz : 42 } } }
  const result = assocPath(
    [ 'foo', 'bar', 'baz' ], 42, { foo : null }
  )
  expect(result).toEqual(expected)
})

---------------

both

 
both(pred1Pred, pred2Pred)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.

const firstCondition = x => x > 10
const secondCondition = x => x < 20
const fn = R.both(secondCondition)
 
const result = [fn(15), fn(30)]
// => [true, false]

Try this R.both example in Rambda REPL

All Typescript definitions
both(pred1Pred, pred2Pred)Pred;
both<T>(pred1Predicate<T>, pred2Predicate<T>)Predicate<T>;
both<T>(pred1Predicate<T>): (pred2: Predicate<T>) => Predicate<T>;
both(pred1Pred): (pred2: Pred) => Pred;
Tests
import { both } from './both'
 
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')
})

---------------

chain

 
chain<T, U>(fn: (n: T) => U[], listreadonly T[])U[]

The method is also known as flatMap.

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
chain<T, U>(fn: (n: T) => U[], listreadonly T[])U[];
chain<T, U>(fn: (n: T) => U[]): (list: readonly T[]) => U[];
chain<X0, X1, R>(fn: (x0: X0, x1: X1) => R, fn1: (x1: X1) => X0): (x1: X1) => R;
Tests
import { chain } from './chain'
 
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 ] ])
})

---------------

clamp

 
clamp(minnumber, maxnumber, inputnumber)number

Restrict a number input to be withing 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.

R.clamp(0, 10, 5) //=> 5
R.clamp(0, 10, -1) //=> 0
R.clamp(0, 10, 11) //=> 10

Try this R.clamp example in Rambda REPL

All Typescript definitions
clamp(minnumber, maxnumber, inputnumber)number;
clamp(minnumber, maxnumber): (input: number) => number;
Tests
import { clamp } from './clamp'
 
test('when min is greater than max', () => {
  expect(() => clamp(
    -5, -10, 5
  )).toThrowWithMessage(Error,
    'min must not be greater than max in clamp(min, max, value)')
})
 
test('rambda specs', () => {
  expect(clamp(
    1, 10, 0
  )).toEqual(1)
  expect(clamp(
    3, 12, 1
  )).toEqual(3)
  expect(clamp(
    -15, 3, -100
  )).toEqual(-15)
  expect(clamp(
    1, 10, 20
  )).toEqual(10)
  expect(clamp(
    3, 12, 23
  )).toEqual(12)
  expect(clamp(
    -15, 3, 16
  )).toEqual(3)
  expect(clamp(
    1, 10, 4
  )).toEqual(4)
  expect(clamp(
    3, 12, 6
  )).toEqual(6)
  expect(clamp(
    -15, 3, 0
  )).toEqual(0)
})

---------------

clone

 
clone<T>(inputT)T

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

const objects = [{a: 1}, {b: 2}];
const objectsClone = R.clone(objects);
 
const result = [
  R.equals(objects, objectsClone),
  R.equals(objects[0], objectsClone[0]),
] // => [ true, true ]

Try this R.clone example in Rambda REPL

All Typescript definitions
clone<T>(inputT)T;
clone<T>(inputreadonly T[])T[];
Tests
import assert from 'assert'
 
import { clone } from './clone'
import { equals } from './equals'
 
test('with array', () => {
  const arr = [
    {
      b : 2,
      c : 'foo',
      d : [ 1, 2, 3 ],
    },
    1,
    new Date(),
    null,
  ]
  expect(clone(arr)).toEqual(arr)
})
 
test('with object', () => {
  const obj = {
    a : 1,
    b : 2,
    c : 3,
    d : [ 1, 2, 3 ],
    e : new Date(),
  }
  expect(clone(obj)).toEqual(obj)
})
 
test('with date', () => {
  const date = new Date(
    2014, 10, 14, 23, 59, 59, 999
  )
 
  const cloned = clone(date)
  assert.notStrictEqual(date, cloned)
  expect(cloned).toEqual(new Date(
    2014, 10, 14, 23, 59, 59, 999
  ))
 
  expect(cloned.getDay()).toEqual(5)
})
 
test('with R.equals', () => {
  const objects = [ { a : 1 }, { b : 2 } ]
 
  const objectsClone = clone(objects)
 
  const result = [
    equals(objects, objectsClone),
    equals(objects[ 0 ], objectsClone[ 0 ]),
  ]
  expect(result).toEqual([ true, true ])
})

---------------

complement

 
complement(pred: (...args: readonly any[]) => boolean): (...args: readonly any[]) => 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).

const origin = x => x > 5
const inverted = complement(origin)
 
const result = [
  origin(7),
  inverted(7)
] => [ true, false ]

Try this R.complement example in Rambda REPL

All Typescript definitions
complement(pred: (...args: readonly any[]) => boolean): (...args: readonly any[]) => boolean;
Tests
import { complement } from './complement'
 
test('happy', () => {
  const fn = complement(x => x.length === 0)
 
  expect(fn([ 1, 2, 3 ])).toBeTrue()
})
 
test('with multiple parameters', () => {
  const between = function (
    a, b, c
  ){
    return a < b && b < c
  }
  const f = complement(between)
  expect(f(
    4, 5, 11
  )).toEqual(false)
  expect(f(
    12, 2, 6
  )).toEqual(true)
})

---------------

compose

 
compose<T1>(fn0: () => T1): () => T1

It performs right-to-left function composition.

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

Try this R.compose example in Rambda REPL

All Typescript definitions
compose<T1>(fn0: () => T1): () => T1;
compose<V0, T1>(fn0: (x0: V0) => T1): (x0: V0) => T1;
compose<V0, V1, T1>(fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T1;
compose<V0, V1, V2, T1>(fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T1;
 
compose<T1, T2>(fn1: (x: T1) => T2, fn0: () => T1): () => T2;
compose<V0, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0) => T1): (x0: V0) => T2;
compose<V0, V1, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T2;
compose<V0, V1, V2, T1, T2>(fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T2;
 
compose<T1, T2, T3>(fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: () => T1): () => T3;
compose<V0, T1, T2, T3>(fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x: V0) => T1): (x: V0) => T3;
compose<V0, V1, T1, T2, T3>(fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T3;
compose<V0, V1, V2, T1, T2, T3>(fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T3;
 
compose<T1, T2, T3, T4>(fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: () => T1): () => T4;
compose<V0, T1, T2, T3, T4>(fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x: V0) => T1): (x: V0) => T4;
compose<V0, V1, T1, T2, T3, T4>(fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T4;
compose<V0, V1, V2, T1, T2, T3, T4>(fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T4;
 
compose<T1, T2, T3, T4, T5>(fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: () => T1): () => T5;
compose<V0, T1, T2, T3, T4, T5>(fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x: V0) => T1): (x: V0) => T5;
compose<V0, V1, T1, T2, T3, T4, T5>(fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T5;
compose<V0, V1, V2, T1, T2, T3, T4, T5>(fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T5;
 
compose<T1, T2, T3, T4, T5, T6>(fn5: (x: T5) => T6, fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: () => T1): () => T6;
compose<V0, T1, T2, T3, T4, T5, T6>(fn5: (x: T5) => T6, fn4: (x: T4) => T5, fn3: (x: T3) => T4, fn2: (x: T2) => T3, fn1: (x: T1) => T2, fn0: (x: V0) => T1): (x: V0) => T6;
compose<V0, V1, T1, T2, T3, T4, T5, T6>(
  fn5: (x: T5) => T6,
  fn4: (x: T4) => T5,
  fn3: (x: T3) => T4,
  fn2: (x: T2) => T3,
  fn1: (x: T1) => T2,
  fn0: (x0: V0, x1: V1) => T1): (x0: V0, x1: V1) => T6;
compose<V0, V1, V2, T1, T2, T3, T4, T5, T6>(
  fn5: (x: T5) => T6,
  fn4: (x: T4) => T5,
  fn3: (x: T3) => T4,
  fn2: (x: T2) => T3,
  fn1: (x: T1) => T2,
  fn0: (x0: V0, x1: V1, x2: V2) => T1): (x0: V0, x1: V1, x2: V2) => T6;
Tests
import { add } from './add'
import { compose } from './compose'
import { filter } from './filter'
import { last } from './last'
import { map } from './map'
 
test('happy', () => {
  const result = compose(
    last, map(add(10)), map(add(1))
  )([ 1, 2, 3 ])
 
  expect(result).toEqual(14)
})
 
test('can accepts initially two arguments', () => {
  const result = compose(map(x => x * 2),
    (list, limit) => filter(x => x > limit, list))([ 1, 2, 3, 4, false ], 2)
 
  expect(result).toEqual([ 6, 8 ])
})
 
test('when no arguments is passed', () => {
  expect(() => compose()).toThrow('compose requires at least one argument')
})
 
test('ramda spec', () => {
  const f = function (
    a, b, c
  ){
    return [ a, b, c ]
  }
  const g = compose(f)
 
  expect(g(
    1, 2, 3
  )).toEqual([ 1, 2, 3 ])
})

---------------

composeAsync

 
composeAsync<Out>(
  ...fns(Async<any> | Func<any>)[]
): (input: any) => Promise<Out>

Asynchronous version of R.compose

💥 It doesn't work with promises or function returning promises such as const foo = input => new Promise(...).

const add = async x => {
  await R.delay(100)
  return x + 1
}
const multiply = async x => {
  await R.delay(100)
  return x * 2 
}
 
const result = await R.composeAsync(
  add,
  multiply
)(1)
// `result` resolves to `3`

Try this R.composeAsync example in Rambda REPL

All Typescript definitions
composeAsync<Out>(
  ...fns(Async<any> | Func<any>)[]
): (input: any) => Promise<Out>;
composeAsync<Out>(
  ...fns(Async<any> | Func<any>)[]
): (input: any) => Promise<Out>;
Tests
import { composeAsync } from './composeAsync'
import { delay } from './delay'
 
async function identity(x){
  await delay(100)
 
  return x
}
 
test('happy', async () => {
  const fn1 = async x => {
    await delay(100)
 
    return x.map(xx => xx + 1)
  }
  const fn2 = async x => {
    await delay(100)
 
    return x.map(xx => xx * 2)
  }
  const result = await composeAsync(fn1,
    fn2)(await Promise.all([ identity(1), identity(2), identity(3) ]))
 
  expect(result).toEqual([ 3, 5, 7 ])
})
 
const delayFn = ms =>
  new Promise(resolve => {
    resolve(ms + 1)
  })
 
test('with function returning promise', async () => {
  const result = await composeAsync(
    x => x,
    x => x + 1,
    delayFn,
    x => x
  )(1)
 
  expect(result).toEqual(3)
})
 
test('throw error', async () => {
  const fn = async () => {