Newman's Personal Motorcade

    rambda
    TypeScript icon, indicating that this package has built-in type declarations

    7.0.1 • Public • Published

    Rambda

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

    CircleCI codecov Commit activity All contributors Library size

    ❯ Example use

    import { compose, map, filter } from 'rambda'
    
    const result = compose(
      map(x => x * 2),
      filter(x => x > 2)
    )([1, 2, 3, 4])
    // => [6, 8]

    You can test this example in Rambda's REPL

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

    ❯ Rambda's advantages

    Typescript included

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

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

    Important - Rambda version 7.0.0(or higher) requires Typescript version 4.2.2(or higher).

    Alternative TS definitions are available as rambda/immutable. These are Rambda definitions linted with ESLint functional/prefer-readonly-type plugin.

    Smaller size

    The size of a library affects not only the build bundle size but also the dev bundle size and build time. This is important advantage, expecially for big projects.

    Tree-shaking

    Currently Rambda is more tree-shakable than Ramda - proven in the following repo.

    The repo holds two Angular9 applications: one with small example code of Ramda and the other - same code but with Rambda as import library.

    The test shows that Rambda bundle size is 2 MB less than its Ramda counterpart.

    There is also Webpack/Rollup/Parcel/Esbuild tree-shaking example including several libraries including Ramda, Rambda and Rambdax.

    actually tree-shaking is the initial reason for creation of Rambda

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

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

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

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

    Comma notation for R.pick and R.omit

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

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

    Speed

    Rambda is generally more performant than Ramda as the benchmarks can prove that.

    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 Rambda to NPM.

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

    ❯ Missing Ramda methods

    Click to see the full list of 85 Ramda methods not implemented in Rambda
    • __
    • addIndex
    • ap
    • aperture
    • applyTo
    • ascend
    • binary
    • 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
    • memoizeWith
    • mergeDeepLeft
    • mergeDeepWith
    • mergeDeepWithKey
    • mergeRight
    • mergeWith
    • mergeWithKey
    • nAry
    • nthArg
    • o
    • otherwise
    • pair
    • partialRight
    • pathSatisfies
    • pickBy
    • pipeK
    • pipeP
    • pipeWith
    • project
    • propSatisfies
    • reduceBy
    • reduceRight
    • reduceWhile
    • reduced
    • remove
    • scan
    • sequence
    • sortWith
    • symmetricDifferenceWith
    • andThen
    • toPairsIn
    • transduce
    • traverse
    • unary
    • uncurryN
    • unfold
    • unionWith
    • uniqBy
    • unnest
    • until
    • useWith
    • valuesIn
    • xprod
    • thunkify
    • default

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

    ❯ Install

    • yarn add rambda

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

    https://unpkg.com/rambda@CURRENT_VERSION/dist/rambda.umd.js
    
    • with deno
    import {compose, add} from 'https://raw.githubusercontent.com/selfrefactor/rambda/master/dist/rambda.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.

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

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

    • Typescript definitions between rambda and @types/ramda may vary.

    If you need more Ramda methods in Rambda, you may either submit a PR or check the extended version of Rambda - Rambdax. 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.21) and Ramda(0.27.1).

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

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

    ❯ Used by

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

    API

    add

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

    It adds a and b.

    All Typescript definitions
    add(a: number, b: number): number;
    add(a: number): (b: number) => number;
    R.add source
    export function add(a, b) {
      if (arguments.length === 1) return _b => add(a, _b)
    
      return Number(a) + Number(b)
    }
    Tests
    import {add} from './add'
    import {add as addRamda} from 'ramda'
    import {compareCombinations} from './_internals/testUtils'
    
    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)
    })
    
    const possibleInputs = [
      /foo/,
      'foo',
      true,
      3,
      NaN,
      4,
      [],
      Promise.resolve(1),
    ]
    
    describe('brute force', () => {
      compareCombinations({
        fn: add,
        fnRamda: addRamda,
        firstInput: possibleInputs,
        secondInput: possibleInputs,
        callback: errorsCounters => {
          expect(errorsCounters).toMatchInlineSnapshot(`
            Object {
              "ERRORS_MESSAGE_MISMATCH": 0,
              "ERRORS_TYPE_MISMATCH": 0,
              "RESULTS_MISMATCH": 0,
              "SHOULD_NOT_THROW": 0,
              "SHOULD_THROW": 0,
              "TOTAL_TESTS": 64,
            }
          `)
        },
      })
    })
    Typescript test
    import {add} from 'rambda'
    
    describe('R.add', () => {
      it('happy', () => {
        const result = add(4, 1)
    
        result // $ExpectType number
      })
      it('curried', () => {
        const result = add(4)(1)
    
        result // $ExpectType number
      })
    })
    Rambda is fastest. Ramda is 21.52% slower and Lodash is 82.15% slower
    const R = require('../../dist/rambda.js')
    
    const add = [
      {
        label: 'Rambda',
        fn: () => {
          R.add(1, 1)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.add(1, 1)
        },
      },
      {
        label: 'Lodash',
        fn: () => {
          _.add(1, 1)
        },
      },
    ]
    

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

    adjust

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

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

    All Typescript definitions
    adjust<T>(index: number, replaceFn: (x: T) => T, list: T[]): T[];
    adjust<T>(index: number, replaceFn: (x: T) => T): (list: T[]) => T[];
    R.adjust source
    import {curry} from './curry'
    import {cloneList} from './_internals/cloneList'
    
    function adjustFn(index, replaceFn, list) {
      const actualIndex = index < 0 ? list.length + index : index
      if (index >= list.length || actualIndex < 0) return list
    
      const clone = cloneList(list)
      clone[actualIndex] = replaceFn(clone[actualIndex])
    
      return clone
    }
    
    export const adjust = curry(adjustFn)
    Tests
    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)
    })
    Rambda is slower than Ramda with 8.48%
    const R = require('../../dist/rambda.js')
    
    const list = [0, 1, 2]
    const fn = x => x + 1
    const index = 1
    
    const adjust = [
      {
        label: 'Rambda',
        fn: () => {
          R.adjust(index, fn, list)
          R.adjust(index, fn)(list)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.adjust(index, fn, list)
          Ramda.adjust(index, fn)(list)
        },
      },
    ]
    

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

    all

    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.

    All Typescript definitions
    all<T>(predicate: (x: T) => boolean, list: T[]): boolean;
    all<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
    R.all source
    export function all(predicate, list) {
      if (arguments.length === 1) return _list => all(predicate, _list)
    
      for (let i = 0; i < list.length; i++) {
        if (!predicate(list[i])) return false
      }
    
      return true
    }
    Tests
    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()
    })
    Typescript test
    import {all} from 'rambda'
    
    describe('all', () => {
      it('happy', () => {
        const result = all(
          x => {
            x // $ExpectType number
            return x > 0
          },
          [1, 2, 3]
        )
        result // $ExpectType boolean
      })
      it('curried needs a type', () => {
        const result = all<number>(x => {
          x // $ExpectType number
          return x > 0
        })([1, 2, 3])
        result // $ExpectType boolean
      })
    })
    Rambda is faster than Ramda with 1.81%
    const R = require('../../dist/rambda.js')
    
    const {
      uniqListOfObjects,
      uniqListOfStrings,
      rangeOfNumbers,
      uniqListOfLists,
    } = require('./_utils.js')
    
    const limit = 100
    
    const modes = [
      [uniqListOfObjects(limit), x => Object.keys(x).length > 2],
      [uniqListOfStrings(limit), x => x.length > 0],
      [uniqListOfLists(limit), x => x.length > 0],
      [rangeOfNumbers(limit), x => x > -1],
    ]
    
    const applyBenchmark = (fn, input) => {
      return fn(input[1], input[0])
    }
    
    const tests = [
      {
        label: 'Rambda',
        fn: R.all,
      },
      {
        label: 'Ramda',
        fn: Ramda.all,
      },
    ]
    

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

    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.

    All Typescript definitions
    allPass<T>(predicates: ((x: T) => boolean)[]): (input: T) => boolean;
    R.allPass source
    export function allPass(predicates) {
      return (...input) => {
        let counter = 0
        while (counter < predicates.length) {
          if (!predicates[counter](...input)) {
            return false
          }
          counter++
        }
    
        return true
      }
    }
    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()
    })
    
    test('works with multiple inputs', () => {
      var fn = function (w, x, y, z) {
        return w + x === y + z
      }
      expect(allPass([fn])(3, 3, 3, 3)).toBeTrue()
    })
    Typescript test
    import {allPass} from 'rambda'
    
    describe('allPass', () => {
      it('happy', () => {
        const x = allPass<number>([
          y => {
            y // $ExpectType number
            return typeof y === 'number'
          },
          y => {
            return y > 0
          },
        ])(11)
    
        x // $ExpectType boolean
      })
    })
    Rambda is faster than Ramda with 91.09%
    const R = require('../../dist/rambda.js')
    
    const {random} = require('rambdax')
    
    const limit = 100
    const min = 10
    const max = 1200
    function createListOfFunctions(fn, fnLimit) {
      return Array(fnLimit)
        .fill(null)
        .map(() => fn())
    }
    
    const modes = [
      [
        {foo: 1500},
        createListOfFunctions(
          () => x => Number(x.foo) > random(min, max),
          limit
        ),
      ],
      [
        '1500',
        createListOfFunctions(() => x => Number(x) > random(min, max), limit),
      ],
      [
        [1, 2, 1500],
        createListOfFunctions(() => x => x[2] > random(min, max), limit),
      ],
      [1500, createListOfFunctions(() => x => x > random(min, max), limit)],
    ]
    
    const applyBenchmark = (fn, input) => {
      return fn(input[1])(input[0])
    }
    const tests = [
      {
        label: 'Rambda',
        fn: R.allPass,
      },
      {
        label: 'Ramda',
        fn: Ramda.allPass,
      },
    ]
    

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

    always

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

    It returns function that always returns x.

    All Typescript definitions
    always<T>(x: T): (...args: unknown[]) => T;
    R.always source
    export function always(x) {
      return () => x
    }
    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()
    })
    Typescript test
    import {always} from 'rambda'
    
    describe('R.always', () => {
      it('happy', () => {
        const fn = always('foo')
        fn // $ExpectType (...args: unknown[]) => string
        const result = fn()
        result // $ExpectType string
      })
    })

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

    and

    and<T, U>(x: T, y: U): T | U

    Logical AND

    All Typescript definitions
    and<T, U>(x: T, y: U): T | U;
    and<T>(x: T): <U>(y: U) => T | U;
    R.and source
    export function and(a, b) {
      if (arguments.length === 1) return _b => and(a, _b)
    
      return a && b
    }
    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()
    })
    Typescript test
    import {and} from 'rambda'
    
    describe('R.and', () => {
      it('happy', () => {
        const result = and(true, false)
        result // $ExpectType boolean
      })
      it('curried', () => {
        const result = and('foo')(1)
        result // $ExpectType string | 1
      })
    })
    Rambda is faster than Ramda with 89.09%
    const R = require('../../dist/rambda.js')
    
    const and = [
      {
        label: 'Rambda',
        fn: () => {
          R.and(true, true)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.and(true, true)
        },
      },
    ]
    

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

    any

    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.

    All Typescript definitions
    any<T>(predicate: (x: T) => boolean, list: T[]): boolean;
    any<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
    R.any source
    export function any(predicate, list) {
      if (arguments.length === 1) return _list => any(predicate, _list)
    
      let counter = 0
      while (counter < list.length) {
        if (predicate(list[counter], counter)) {
          return true
        }
        counter++
      }
    
      return false
    }
    Tests
    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()
    })
    Typescript test
    import {any} from 'rambda'
    
    describe('R.any', () => {
      it('happy', () => {
        const result = any(
          x => {
            x // $ExpectType number
            return x > 2
          },
          [1, 2, 3]
        )
        result // $ExpectType boolean
      })
    
      it('when curried needs a type', () => {
        const result = any<number>(x => {
          x // $ExpectType number
          return x > 2
        })([1, 2, 3])
        result // $ExpectType boolean
      })
    })
    Rambda is fastest. Ramda is 92.87% slower and Lodash is 45.82% slower
    const R = require('../../dist/rambda.js')
    
    const input = [1, 2, 3, 4]
    const fn = val => val > 2
    
    const any = [
      {
        label: 'Rambda',
        fn: () => {
          R.any(fn, input)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.any(fn, input)
        },
      },
      {
        label: 'Lodash.some',
        fn: () => {
          _.some(input, fn)
        },
      },
    ]
    

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

    anyPass

    anyPass<T>(predicates: SafePred<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.

    All Typescript definitions
    anyPass<T>(predicates: SafePred<T>[]): SafePred<T>;
    R.anyPass source
    export function anyPass(predicates) {
      return (...input) => {
        let counter = 0
        while (counter < predicates.length) {
          if (predicates[counter](...input)) {
            return true
          }
          counter++
        }
    
        return false
      }
    }
    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('with empty predicates list', () => {
      expect(anyPass([])(3)).toEqual(false)
    })
    
    test('works with multiple inputs', () => {
      var fn = function (w, x, y, z) {
        console.log(w, x, y, z)
        return w + x === y + z
      }
      expect(anyPass([fn])(3, 3, 3, 3)).toBeTrue()
    })
    Typescript test
    import {anyPass} from 'rambda'
    
    describe('anyPass', () => {
      it('happy', () => {
        const x = anyPass<number>([
          y => {
            y // $ExpectType number
            return typeof y === 'number'
          },
          y => {
            return y > 0
          },
        ])(11)
    
        x // $ExpectType boolean
      })
    })
    Rambda is faster than Ramda with 98.25%
    const R = require('../../dist/rambda.js')
    
    const rules = [x => typeof x === 'boolean', x => x > 20, x => x * 7 < 100]
    
    const anyPass = [
      {
        label: 'Rambda',
        fn: () => {
          R.anyPass(rules)(11)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.anyPass(rules)(11)
        },
      },
    ]
    

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

    append

    append<T>(x: T, list: T[]): T[]

    It adds element x at the end of list.

    All Typescript definitions
    append<T>(x: T, list: T[]): T[];
    append<T>(x: T): <T>(list: T[]) => T[];
    R.append source
    import {cloneList} from './_internals/cloneList'
    
    export function append(x, input) {
      if (arguments.length === 1) return _input => append(x, _input)
    
      if (typeof input === 'string') return input.split('').concat(x)
    
      const clone = cloneList(input)
      clone.push(x)
    
      return clone
    }
    Tests
    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'])
    })
    Typescript test
    import {append} from 'rambda'
    
    const list = [1, 2, 3]
    
    describe('R.append', () => {
      it('happy', () => {
        const result = append(4, list)
    
        result // $ExpectType number[]
      })
      it('curried', () => {
        const result = append(4)(list)
    
        result // $ExpectType number[]
      })
    })
    Rambda is faster than Ramda with 2.07%
    const R = require('../../dist/rambda.js')
    
    const append = [
      {
        label: 'Rambda',
        fn: () => {
          R.append(0)([1, 2, 3, 4])
          R.append('bar')('foo')
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.append(0)([1, 2, 3, 4])
          Ramda.append('bar')('foo')
        },
      },
    ]
    

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

    apply

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

    It applies function fn to the list of arguments.

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

    All Typescript definitions
    apply<T = any>(fn: (...args: any[]) => T, args: any[]): T;
    apply<T = any>(fn: (...args: any[]) => T): (args: any[]) => T;
    R.apply source
    export function apply(fn, args) {
      if (arguments.length === 1) {
        return _args => apply(fn, _args)
      }
    
      return fn.apply(this, args)
    }
    Tests
    import {apply} from './apply'
    import {bind} from './bind'
    import {identity} from './identity'
    
    test('happy', () => {
      expect(apply(identity, [1, 2, 3])).toEqual(1)
    })
    
    test('applies function to argument list', function () {
      expect(apply(Math.max, [1, 2, 3, -99, 42, 6, 7])).toEqual(42)
    })
    
    test('provides no way to specify context', function () {
      const obj = {
        method: function () {
          return this === obj
        },
      }
      expect(apply(obj.method, [])).toEqual(false)
      expect(apply(bind(obj.method, obj), [])).toEqual(true)
    })
    Typescript test
    import {apply, identity} from 'rambda'
    
    describe('R.apply', () => {
      it('happy', () => {
        const result = apply<number>(identity, [1, 2, 3])
    
        result // $ExpectType number
      })
      it('curried', () => {
        const fn = apply<number>(identity)
        const result = fn([1, 2, 3])
    
        result // $ExpectType number
      })
    })

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

    applySpec

    applySpec<Spec extends Record<string, (...args: any[]) => any>>(
      spec: Spec
    ): (
      ...args: Parameters<ValueOfRecord<Spec>>
    ) => { [Key in keyof Spec]: ReturnType<Spec[Key]> }
    All Typescript definitions
    applySpec<Spec extends Record<string, (...args: any[]) => any>>(
      spec: Spec
    ): (
      ...args: Parameters<ValueOfRecord<Spec>>
    ) => { [Key in keyof Spec]: ReturnType<Spec[Key]> };
    applySpec<T>(spec: any): (...args: any[]) => T;
    R.applySpec source
    import {_isArray} from './_internals/_isArray'
    
    // recursively traverse the given spec object to find the highest arity function
    function __findHighestArity(spec, max = 0) {
      for (const key in spec) {
        if (spec.hasOwnProperty(key) === false || key === 'constructor') continue
    
        if (typeof spec[key] === 'object') {
          max = Math.max(max, __findHighestArity(spec[key]))
        }
    
        if (typeof spec[key] === 'function') {
          max = Math.max(max, spec[key].length)
        }
      }
    
      return max
    }
    
    function __filterUndefined() {
      const defined = []
      let i = 0
      const l = arguments.length
      while (i < l) {
        if (typeof arguments[i] === 'undefined') break
        defined[i] = arguments[i]
        i++
      }
    
      return defined
    }
    
    function __applySpecWithArity(spec, arity, cache) {
      const remaining = arity - cache.length
    
      if (remaining === 1)
        return x =>
          __applySpecWithArity(spec, arity, __filterUndefined(...cache, x))
      if (remaining === 2)
        return (x, y) =>
          __applySpecWithArity(spec, arity, __filterUndefined(...cache, x, y))
      if (remaining === 3)
        return (x, y, z) =>
          __applySpecWithArity(spec, arity, __filterUndefined(...cache, x, y, z))
      if (remaining === 4)
        return (x, y, z, a) =>
          __applySpecWithArity(
            spec,
            arity,
            __filterUndefined(...cache, x, y, z, a)
          )
      if (remaining > 4)
        return (...args) =>
          __applySpecWithArity(spec, arity, __filterUndefined(...cache, ...args))
    
      // handle spec as Array
      if (_isArray(spec)) {
        const ret = []
        let i = 0
        const l = spec.length
        for (; i < l; i++) {
          // handle recursive spec inside array
          if (typeof spec[i] === 'object' || _isArray(spec[i])) {
            ret[i] = __applySpecWithArity(spec[i], arity, cache)
          }
          // apply spec to the key
          if (typeof spec[i] === 'function') {
            ret[i] = spec[i](...cache)
          }
        }
    
        return ret
      }
    
      // handle spec as Object
      const ret = {}
      // apply callbacks to each property in the spec object
      for (const key in spec) {
        if (spec.hasOwnProperty(key) === false || key === 'constructor') continue
    
        // apply the spec recursively
        if (typeof spec[key] === 'object') {
          ret[key] = __applySpecWithArity(spec[key], arity, cache)
          continue
        }
    
        // apply spec to the key
        if (typeof spec[key] === 'function') {
          ret[key] = spec[key](...cache)
        }
      }
    
      return ret
    }
    
    export function applySpec(spec, ...args) {
      // get the highest arity spec function, cache the result and pass to __applySpecWithArity
      const arity = __findHighestArity(spec)
    
      if (arity === 0) {
        return () => ({})
      }
      const toReturn = __applySpecWithArity(spec, arity, args)
    
      return toReturn
    }
    Tests
    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',
      })
    })
    Typescript test
    import {multiply, applySpec, inc, dec, add} from 'rambda'
    
    describe('applySpec', () => {
      it('ramda 1', () => {
        const result = applySpec({
          v: inc,
          u: dec,
        })(1)
        result // $ExpectType { v: number; u: number; }
      })
      it('ramda 1', () => {
        interface Output {
          sum: number,
          multiplied: number,
        }
        const result = applySpec<Output>({
          sum: add,
          multiplied: multiply,
        })(1, 2)
    
        result // $ExpectType Output
      })
    })
    Rambda is faster than Ramda with 80.43%
    const R = require('../../dist/rambda.js')
    
    const curryN = [
      {
        label: 'Rambda',
        fn: () => {
          const data = {
            a: {
              b: {c: 1},
              d: 2,
            },
          }
          const spec = {
            c: R.path(['a', 'b', 'c']),
            d: R.path(['a', 'd']),
          }
          R.applySpec(spec, data)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          const data = {
            a: {
              b: {c: 1},
              d: 2,
            },
          }
          const spec = {
            c: Ramda.path(['a', 'b', 'c']),
            d: Ramda.path(['a', 'd']),
          }
          Ramda.applySpec(spec, data)
        },
      },
    ]
    

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

    assoc

    assoc<T, U, K extends string>(prop: K, val: T, obj: U): Record<K, T> & Omit<U, K>

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

    All Typescript definitions
    assoc<T, U, K extends string>(prop: K, val: T, obj: U): Record<K, T> & Omit<U, K>;
    assoc<T, K extends string>(prop: K, val: T): <U>(obj: U) => Record<K, T> & Omit<U, K>;
    assoc<K extends string>(prop: K): AssocPartialOne<K>;
    R.assoc source
    import {curry} from './curry'
    
    function assocFn(prop, newValue, obj) {
      return Object.assign({}, obj, {[prop]: newValue})
    }
    
    export const assoc = curry(assocFn)
    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}})
    })
    Typescript test
    import {assoc} from 'rambda'
    
    const obj = {a: 1}
    const newValue = 2
    const newProp = 'b'
    
    describe('R.assoc', () => {
      it('happy', () => {
        const result = assoc(newProp, newValue, obj)
    
        result.a // $ExpectType number
        result.b // $ExpectType number
      })
      it('curried 1', () => {
        const result = assoc(newProp, newValue)(obj)
    
        result.a // $ExpectType number
        result.b // $ExpectType number
      })
      it('curried 2', () => {
        const result = assoc(newProp)(newValue)(obj)
    
        result.a // $ExpectType number
        result.b // $ExpectType number
      })
    })
    Lodash is fastest. Rambda is 72.32% slower and Ramda is 60.08% slower
    const R = require('../../dist/rambda.js')
    
    const input = {
      a: 1,
      b: 2,
    }
    const key = 'c'
    const value = 3
    
    const assoc = [
      {
        label: 'Rambda',
        fn: () => {
          R.assoc(key, value, input)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.assoc(key, value, input)
        },
      },
      {
        label: 'Lodash.set',
        fn: () => {
          _.set(input, key, value)
        },
      },
    ]
    

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

    assocPath

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

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

    All Typescript definitions
    assocPath<Output>(path: Path, newValue: any, obj: object): Output;
    assocPath<Output>(path: Path, newValue: any): (obj: object) => Output;
    assocPath<Output>(path: Path): (newValue: any) => (obj: object) => Output;
    R.assocPath source
    import {_isArray} from './_internals/_isArray'
    import {_isInteger} from './_internals/_isInteger'
    import {assoc} from './assoc'
    import {curry} from './curry'
    import {cloneList} from './_internals/cloneList'
    
    function assocPathFn(path, newValue, input) {
      const pathArrValue =
        typeof path === 'string'
          ? path.split('.').map(x => (_isInteger(Number(x)) ? Number(x) : x))
          : path
      if (pathArrValue.length === 0) {
        return newValue
      }
    
      const index = pathArrValue[0]
      if (pathArrValue.length > 1) {
        const condition =
          typeof input !== 'object' ||
          input === null ||
          !input.hasOwnProperty(index)
    
        const nextinput = condition
          ? _isInteger(pathArrValue[1])
            ? []
            : {}
          : input[index]
    
        newValue = assocPathFn(
          Array.prototype.slice.call(pathArrValue, 1),
          newValue,
          nextinput
        )
      }
    
      if (_isInteger(index) && _isArray(input)) {
        const arr = cloneList(input)
        arr[index] = newValue
    
        return arr
      }
    
      return assoc(index, newValue, input)
    }
    
    export const assocPath = curry(assocPathFn)
    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)
    })
    Typescript test
    import {assocPath} from 'rambda'
    
    interface Output {
      a: number,
      foo: {bar: number},
    }
    
    describe('R.assocPath - user must explicitly set type of output', () => {
      it('with array as path input', () => {
        const result = assocPath<Output>(['foo', 'bar'], 2, {a: 1})
    
        result // $ExpectType Output
      })
      it('with string as path input', () => {
        const result = assocPath<Output>('foo.bar', 2, {a: 1})
    
        result // $ExpectType Output
      })
    })
    
    describe('R.assocPath - curried', () => {
      it('with array as path input', () => {
        const result = assocPath<Output>(['foo', 'bar'], 2)({a: 1})
    
        result // $ExpectType Output
      })
      it('with string as path input', () => {
        const result = assocPath<Output>('foo.bar', 2)({a: 1})
    
        result // $ExpectType Output
      })
    })

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

    bind

    bind<F extends (...args: any[]) => any, T>(fn: F, thisObj: T): (...args: Parameters<F>) => ReturnType<F>

    Creates a function that is bound to a context.

    All Typescript definitions
    bind<F extends (...args: any[]) => any, T>(fn: F, thisObj: T): (...args: Parameters<F>) => ReturnType<F>;
    bind<F extends (...args: any[]) => any, T>(fn: F): (thisObj: T) => (...args: Parameters<F>) => ReturnType<F>;
    R.bind source
    import {curryN} from './curryN'
    
    export function bind(fn, thisObj) {
      if (arguments.length === 1) {
        return _thisObj => bind(fn, _thisObj)
      }
    
      return curryN(fn.length, (...args) => fn.apply(thisObj, args))
    }
    Tests
    import {bind} from './bind'
    
    function Foo(x) {
      this.x = x
    }
    function add(x) {
      return this.x + x
    }
    function Bar(x, y) {
      this.x = x
      this.y = y
    }
    Bar.prototype = new Foo()
    Bar.prototype.getX = function () {
      return 'prototype getX'
    }
    
    test('returns a function', function () {
      expect(typeof bind(add)(Foo)).toEqual('function')
    })
    
    test('returns a function bound to the specified context object', function () {
      const f = new Foo(12)
      function isFoo() {
        return this instanceof Foo
      }
      const isFooBound = bind(isFoo, f)
      expect(isFoo()).toEqual(false)
      expect(isFooBound()).toEqual(true)
    })
    
    test('works with built-in types', function () {
      const abc = bind(String.prototype.toLowerCase, 'ABCDEFG')
      expect(typeof abc).toEqual('function')
      expect(abc()).toEqual('abcdefg')
    })
    
    test('works with user-defined types', function () {
      const f = new Foo(12)
      function getX() {
        return this.x
      }
      const getXFooBound = bind(getX, f)
      expect(getXFooBound()).toEqual(12)
    })
    
    test('works with plain objects', function () {
      const pojso = {
        x: 100,
      }
      function incThis() {
        return this.x + 1
      }
      const incPojso = bind(incThis, pojso)
      expect(typeof incPojso).toEqual('function')
      expect(incPojso()).toEqual(101)
    })
    
    test('does not interfere with existing object methods', function () {
      const b = new Bar('a', 'b')
      function getX() {
        return this.x
      }
      const getXBarBound = bind(getX, b)
      expect(b.getX()).toEqual('prototype getX')
      expect(getXBarBound()).toEqual('a')
    })
    
    test('preserves arity', function () {
      const f0 = function () {
        return 0
      }
      const f1 = function (a) {
        return a
      }
      const f2 = function (a, b) {
        return a + b
      }
      const f3 = function (a, b, c) {
        return a + b + c
      }
    
      expect(bind(f0, {}).length).toEqual(0)
      expect(bind(f1, {}).length).toEqual(1)
      expect(bind(f2, {}).length).toEqual(2)
      expect(bind(f3, {}).length).toEqual(3)
    })
    Typescript test
    import {bind} from 'rambda'
    
    class Foo {}
    function isFoo<T = any>(this: T): boolean {
      return this instanceof Foo
    }
    
    describe('R.bind', () => {
      it('happy', () => {
        const foo = new Foo()
        const result = bind(isFoo, foo)()
    
        result // $ExpectType boolean
      })
    })

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

    both

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

    It returns a function with input argument.

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

    All Typescript definitions
    both(pred1: Pred, pred2: Pred): Pred;
    both<T>(pred1: Predicate<T>, pred2: Predicate<T>): Predicate<T>;
    both<T>(pred1: Predicate<T>): (pred2: Predicate<T>) => Predicate<T>;
    both(pred1: Pred): (pred2: Pred) => Pred;
    R.both source
    export function both(f, g) {
      if (arguments.length === 1) return _g => both(f, _g)
    
      return (...input) => f(...input) && g(...input)
    }
    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')
    })
    Typescript test
    import {both} from 'rambda'
    
    describe('R.both', () => {
      it('with passed type', () => {
        const fn = both<number>(
          x => x > 1,
          x => x % 2 === 0
        )
        fn // $ExpectType Predicate<number>
        const result = fn(2) // $ExpectType boolean
        result // $ExpectType boolean
      })
      it('with passed type - curried', () => {
        const fn = both<number>(x => x > 1)(x => x % 2 === 0)
        fn // $ExpectType Predicate<number>
        const result = fn(2)
        result // $ExpectType boolean
      })
      it('no type passed', () => {
        const fn = both(
          x => {
            x // $ExpectType any
            return x > 1
          },
          x => {
            x // $ExpectType any
            return x % 2 === 0
          }
        )
        const result = fn(2)
        result // $ExpectType boolean
      })
      it('no type passed - curried', () => {
        const fn = both((x: number) => {
          x // $ExpectType number
          return x > 1
        })((x: number) => {
          x // $ExpectType number
          return x % 2 === 0
        })
        const result = fn(2)
        result // $ExpectType boolean
      })
    })

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

    chain

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

    The method is also known as flatMap.

    All Typescript definitions
    chain<T, U>(fn: (n: T) => U[], list: T[]): U[];
    chain<T, U>(fn: (n: T) => U[]): (list: T[]) => U[];
    R.chain source
    export function chain(fn, list) {
      if (arguments.length === 1) {
        return _list => chain(fn, _list)
      }
    
      return [].concat(...list.map(fn))
    }
    Tests
    import {chain} from './chain'
    import {chain as chainRamda} from 'ramda'
    
    const duplicate = n => [n, n]
    
    test('happy', () => {
      const fn = x => [x * 2]
      const list = [1, 2, 3]
    
      const result = chain(fn, list)
    
      expect(result).toEqual([2, 4, 6])
    })
    
    test('maps then flattens one level', () => {
      expect(chain(duplicate, [1, 2, 3])).toEqual([1, 1, 2, 2, 3, 3])
    })
    
    test('maps then flattens one level - curry', () => {
      expect(chain(duplicate)([1, 2, 3])).toEqual([1, 1, 2, 2, 3, 3])
    })
    
    test('flattens only one level', () => {
      const nest = n => [[n]]
      expect(chain(nest, [1, 2, 3])).toEqual([[1], [2], [3]])
    })
    
    test('can compose', () => {
      function dec(x) {
        return [x - 1]
      }
      function times2(x) {
        return [x * 2]
      }
    
      var mdouble = chain(times2)
      var mdec = chain(dec)
      expect(mdec(mdouble([10, 20, 30]))).toEqual([19, 39, 59])
    })
    
    test('@types/ramda broken test', () => {
      const score = {
        maths: 90,
        physics: 80,
      }
    
      const calculateTotal = score => {
        const {maths, physics} = score
        return maths + physics
      }
    
      const assocTotalToScore = (total, score) => ({...score, total})
    
      const calculateAndAssocTotalToScore = chainRamda(
        assocTotalToScore,
        calculateTotal
      )
      expect(() => calculateAndAssocTotalToScore(score)).toThrow()
    })
    Typescript test
    import {chain} from 'rambda'
    
    const list = [1, 2, 3]
    const fn = (x: number) => [`${x}`, `${x}`]
    
    describe('R.chain', () => {
      it('without passing type', () => {
        const result = chain(fn, list)
        result // $ExpectType string[]
    
        const curriedResult = chain(fn)(list)
        curriedResult // $ExpectType string[]
      })
    })

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

    clamp

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

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

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

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

    All Typescript definitions
    clamp(min: number, max: number, input: number): number;
    clamp(min: number, max: number): (input: number) => number;
    R.clamp source
    import {curry} from './curry'
    
    function clampFn(min, max, input) {
      if (min > max) {
        throw new Error(
          'min must not be greater than max in clamp(min, max, value)'
        )
      }
      if (input >= min && input <= max) return input
    
      if (input > max) return max
      if (input < min) return min
    }
    
    export const clamp = curry(clampFn)
    Tests
    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)
    })
    Typescript test
    import {clamp} from 'rambda'
    
    describe('R.clamp', () => {
      it('happy', () => {
        const result = clamp(1, 10, 20)
        result // $ExpectType number
      })
    })

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

    clone

    clone<T>(input: T): T

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

    All Typescript definitions
    clone<T>(input: T): T;
    clone<T>(input: T[]): T[];
    R.clone source
    import {_isArray} from './_internals/_isArray'
    
    export function clone(input) {
      const out = _isArray(input) ? Array(input.length) : {}
      if (input && input.getTime) return new Date(input.getTime())
    
      for (const key in input) {
        const v = input[key]
        out[key] =
          typeof v === 'object' && v !== null
            ? v.getTime
              ? new Date(v.getTime())
              : clone(v)
            : v
      }
    
      return out
    }
    Tests
    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])
    })
    Typescript test
    import {clone} from 'rambda'
    
    describe('R.clone', () => {
      it('happy', () => {
        const obj = {a: 1, b: 2}
        const result = clone(obj)
        result // $ExpectType { a: number; b: number; }
      })
    })
    Rambda is fastest. Ramda is 91.86% slower and Lodash is 86.48% slower
    const R = require('../../dist/rambda.js')
    
    const input = {
      a: 1,
      b: 2,
    }
    
    const clone = [
      {
        label: 'Rambda',
        fn: () => {
          R.clone(input)
        },
      },
      {
        label: 'Ramda',
        fn: () => {
          Ramda.clone(input)
        },
      },
      {
        label: 'Lodash.cloneDeep',
        fn: () => {
          _.cloneDeep(input)
        },
      },
    ]
    

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

    complement

    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).

    All Typescript definitions
    complement<T extends any[]>(predicate: (...args: T) => unknown): (...args: T) => boolean;
    R.complement source
    export function complement(fn) {
      return (...input) => !fn(...input)
    }
    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)
    })
    Typescript test
    import {complement, isNil} from 'rambda'
    
    describe('R.complement', () => {
      it('happy', () => {
        const fn = complement(isNil)
        const result = fn(null)
        result // $ExpectType boolean
      })
    })

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

    compose

    It performs right-to-left function composition.

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

    concat

    concat<T>(x: T[], y: T[]): T[]

    It returns a new string or array, which is the result of merging x and y.

    All Typescript definitions
    concat<T>(x: T[], y: T[]): T[];
    concat<T>(x: T[]): (y: T[]) => T[];
    concat(x: string, y: string): string;
    concat(x: string): (y: string) => string;
    R.concat source
    export function concat(x, y) {
      if (arguments.length === 1) return _y => concat(x, _y)
    
      return typeof x === 'string' ? `${x}${y}` : [...x, ...y]
    }
    Tests
    import {concat} from './concat'
    
    test('happy', () => {
      const arr1 = ['a', 'b', 'c']
      const arr2 = ['d', 'e', 'f']
    
      const a = concat(arr1, arr2)
      const b = concat(arr1)(arr2)
      const expectedResult = ['a', 'b', 'c', 'd', 'e', 'f']
    
      expect(a).toEqual(expectedResult)
      expect(b).toEqual(expectedResult)
    })
    
    test('with strings', () => {
      expect(concat('ABC', 'DEF')).toEqual('ABCDEF')
    })
    Typescript test
    import {concat} from 'rambda'
    
    const list1 = [1, 2, 3]
    const list2 = [4, 5, 6]
    
    describe('R.concat', () => {
      it('happy', () => {
        const result = concat(list1, list2)
    
        result // $ExpectType number[]
      })
      it('curried', () => {
        const result = concat(list1)(list2)
    
        result // $ExpectType number[]
      })
    })

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

    cond

    cond<T extends any[], R>(conditions: Array<CondPair<T, R>>): (...args: T) => R

    It takes list with conditions and returns a new function fn that expects input as argument.

    This function will start evaluating the conditions in order to find the first winner(order of conditions matter).

    The winner is this condition, which left side returns true when input is its argument. Then the evaluation of the right side of the winner will be the final result.

    If no winner is found, then fn returns undefined.

    All Typescript definitions
    cond<T extends any[], R>(conditions: Array<CondPair<T, R>>): (...args: T) => R;
    R.cond source
    export function cond(conditions) {
      return input => {
        let done = false
        let toReturn
        conditions.forEach(([predicate, resultClosure]) => {
          if (!done && predicate(input)) {
            done = true
            toReturn = resultClosure(input)
          }
        })
    
        return toReturn
      }
    }
    Tests
    import {always} from './always'
    import {cond} from './cond'
    import {equals} from './equals'
    import {T} from './T'
    
    test('returns a function', () => {
      expect(typeof cond([])).toEqual('function')
    })
    
    test('returns a conditional function', () => {
      const fn = cond([
        [equals(0), always('water freezes at 0°C')],
        [equals(100), always('water boils at 100°C')],
        [
          T,
          function (temp) {
            return 'nothing special happens at ' + temp + '°C'
          },
        ],
      ])
      expect(fn(0)).toEqual('water freezes at 0°C')
      expect(fn(50)).toEqual('nothing special happens at 50°C')
      expect(fn(100)).toEqual('water boils at 100°C')
    })
    
    test('no winner', () => {
      const fn = cond([
        [equals('foo'), always(1)],
        [equals('bar'), always(2)],
      ])
      expect(fn('quux')).toEqual(undefined)
    })
    
    test('predicates are tested in order', () => {
      const fn = cond([
        [T, always('foo')],
        [T, always('bar')],
        [T, always('baz')],
      ])
      expect(fn()).toEqual('foo')
    })
    Typescript test
    import {cond, always, equals} from 'rambda'
    
    describe('R.cond', () => {
      it('happy', () => {
        const fn = cond<number[], string>([
          [equals(0), always('water freezes at 0°C')],
          [equals(100), always('water boils at 100°C')],
          [
            () => true,
            function(temp) {
              temp // $ExpectType number
              

    Keywords

    Install

    npm i rambda

    DownloadsWeekly Downloads

    26,227

    Version

    7.0.1

    License

    MIT

    Unpacked Size

    815 kB

    Total Files

    196

    Last publish

    Collaborators

    • self_refactor