UNPKG

rambda

Version:

Lightweight faster alternative to Ramda

2,241 lines (1,739 loc) 388 kB
# Rambda `Rambda` is faster and smaller alternative to the popular functional programming library **Ramda**. - [Documentation](https://selfrefactor.github.io/rambda/#/) [![CircleCI](https://circleci.com/gh/selfrefactor/rambda/tree/master.svg?style=svg)](https://circleci.com/gh/selfrefactor/rambda/tree/master) [![codecov](https://codecov.io/gh/selfrefactor/rambda/branch/master/graph/badge.svg)](https://codecov.io/gh/selfrefactor/rambda) [![dependencies Status](https://david-dm.org/selfrefactor/rambda/status.svg)](https://david-dm.org/selfrefactor/rambda) ![Normal size](https://img.badgesize.io/selfrefactor/rambda/master/dist/rambda.js) ![Gzip size](https://img.badgesize.io/selfrefactor/rambda/master/dist/rambda.js?compression=gzip) ## Example use ```javascript import { compose, map, filter } from 'rambda' const result = compose( map(x => x * 2), filter(x => x > 2) )([1, 2, 3, 4]) // => [6, 8] ``` You can test this example in <a href="https://rambda.now.sh?const%20result%20%3D%20R.compose(%0A%20%20R.map(x%20%3D%3E%20x%20*%202)%2C%0A%20%20R.filter(x%20%3D%3E%20x%20%3E%202)%0A)(%5B1%2C%202%2C%203%2C%204%5D)%0A%0A%2F%2F%20%3D%3E%20%5B6%2C%208%5D">Rambda's REPL</a> * [Install](#install) * [Differences between Rambda and Ramda](#differences-between-rambda-and-ramda) * [API](#api) * [Changelog](#changelog) ## Rambda's advantages - Tree-shaking Currently **Rambda** is more tree-shakable than **Ramda** - Speed **Rambda** is generally more performant than `Ramda` as the [benchmarks](#benchmarks) can prove that. - dot notation for `R.path` and `R.paths` 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 ``` - Typescript included Typescript definitions are included in the library, in comparison to **Ramda**, where you need to additionally install `@types/ramda`. - More generic methods `Ramda` has an overwhelming list of methods, as one could get lost putting all these methods in one's head. `Rambda` has smaller method counts and that could be seen as advantage. <details> <summary> Click to see the full list of 118 Ramda methods not implemented in Rambda </summary> - __ - addIndex - ap - aperture - apply - applyTo - ascend - binary - bind - call - comparator - composeK - composeP - composeWith - construct - constructN - contains - countBy - descend - differenceWith - dissocPath - dropLastWhile - dropRepeats - dropRepeatsWith - dropWhile - empty - eqBy - eqProps - evolve - forEachObjIndexed - gt - gte - hasIn - hasPath - innerJoin - insert - insertAll - into - invert - invertObj - invoker - juxt - keysIn - lift - liftN - lt - lte - mapAccum - mapAccumRight - mapObjIndexed - mathMod - memoizeWith - mergeAll - mergeDeepLeft - mergeDeepRight - mergeDeepWith - mergeDeepWithKey - mergeLeft - mergeRight - mergeWith - mergeWithKey - move - nAry - nthArg - o - objOf - of - once - or - otherwise - pair - partialRight - partition - pathEq - pathSatisfies - pickBy - pipeK - pipeP - pipeWith - project - propSatisfies - props - reduceBy - reduceRight - reduceWhile - reduced - remove - scan - sequence - sortWith - splitAt - splitWhen - symmetricDifferenceWith - takeLastWhile - takeWhile - andThen - toPairsIn - transduce - traverse - tryCatch - unapply - unary - uncurryN - unfold - union - unionWith - uniqBy - unless - unnest - until - useWith - valuesIn - where - whereEq - xprod - zipWith - thunkify - default </details> ## Install - **yarn add rambda** - For UMD usage either use `./dist/rambda.umd.js` or following CDN link: ``` https://unpkg.com/rambda@CURRENT_VERSION/dist/rambda.umd.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 **path** and **paths** accept dot notation - `'x.y' same as ['x','y']` - Rambda's **pick** and **omit** accept comma notation - `'x,y' same as ['x','y']` - Rambda's **map**, **reject** and **forEach** can iterate over objects not only arrays. - Rambda's **map** and **filter** pass array index as second argument when mapping over arrays. - Rambda's **adjust**, **all**, **allPass**, **any**, **anyPass**, **findIndex** , **findLastIndex** and **reject** are passing index as second argument to the predicate function. - Rambda's **filter** returns empty array with bad input(`null` or `undefined`), while Ramda throws. - Ramda's **includes** will throw an error if input is neither `string` nor `array`, while **Rambda** version will return `false`. - Ramda's **clamp** work for letters, while Rambda's method work only for numbers. > If you need more **Ramda** methods in **Rambda**, you may either submit a `PR` or check the extended version of **Rambda** - [Rambdax](https://github.com/selfrefactor/rambdax). In case of the former, you may want to consult with [Rambda contribution guidelines.](CONTRIBUTING.md) ## Benchmarks <details> <summary> 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.15) and *Ramda*(0.27.0). </summary> method | Rambda | Ramda | Lodash --- |--- | --- | --- *add* | 96.31% slower | 96.28% slower | 🚀 Fastest *adjust* | 🚀 Fastest | 3.15% slower | 🔳 *all* | 🚀 Fastest | 94.35% slower | 🔳 *allPass* | 🚀 Fastest | 98.94% slower | 🔳 *any* | 🚀 Fastest | 98.13% slower | 1.44% slower *anyPass* | 🚀 Fastest | 99.09% slower | 🔳 *append* | 🚀 Fastest | 82.6% slower | 🔳 *applySpec* | 🚀 Fastest | 66.53% slower | 🔳 *assoc* | 86.73% slower | 52.15% slower | 🚀 Fastest *clone* | 🚀 Fastest | 82.43% slower | 62.59% slower *compose* | 🚀 Fastest | 95.45% slower | 73.78% slower *converge* | 44.9% slower | 🚀 Fastest | 🔳 *curry* | 🚀 Fastest | 31.93% slower | 🔳 *curryN* | 55.07% slower | 🚀 Fastest | 🔳 *defaultTo* | 🚀 Fastest | 62.94% slower | 🔳 *drop* | 🚀 Fastest | 97.08% slower | 🔳 *dropLast* | 🚀 Fastest | 96.68% slower | 🔳 *equals* | 76.94% slower | 78.52% slower | 🚀 Fastest *filter* | 72.76% slower | 87.36% slower | 🚀 Fastest *find* | 🚀 Fastest | 91.33% slower | 51.26% slower *findIndex* | 🚀 Fastest | 97.9% slower | 76.76% slower *flatten* | 95.68% slower | 94.49% slower | 🚀 Fastest *ifElse* | 🚀 Fastest | 63.06% slower | 🔳 *includes* | 🚀 Fastest | 68.05% slower | 🔳 *indexOf* | 7.01% slower | 85.46% slower | 🚀 Fastest *init* | 94.02% slower | 96.66% slower | 🚀 Fastest *is* | 🚀 Fastest | 12.21% slower | 🔳 *isEmpty* | 62.71% slower | 93.35% slower | 🚀 Fastest *last* | 3.21% slower | 99.62% slower | 🚀 Fastest *lastIndexOf* | 🚀 Fastest | 41.73% slower | 🔳 *map* | 30.91% slower | 63.2% slower | 🚀 Fastest *match* | 🚀 Fastest | 41.44% slower | 🔳 *merge* | 62.29% slower | 🚀 Fastest | 52.15% slower *none* | 🚀 Fastest | 87.49% slower | 🔳 *omit* | 🚀 Fastest | 71.15% slower | 97.62% slower *over* | 🚀 Fastest | 57.7% slower | 🔳 *path* | 6.33% slower | 74.51% slower | 🚀 Fastest *pick* | 🚀 Fastest | 22.61% slower | 85.66% slower *prop* | 🚀 Fastest | 88.53% slower | 🔳 *propEq* | 🚀 Fastest | 93.89% slower | 🔳 *range* | 95.33% slower | 90.36% slower | 🚀 Fastest *reduce* | 56.8% slower | 72.82% slower | 🚀 Fastest *repeat* | 84.33% slower | 95.02% slower | 🚀 Fastest *replace* | 1.27% slower | 31.5% slower | 🚀 Fastest *set* | 🚀 Fastest | 47.8% slower | 🔳 *sort* | 🚀 Fastest | 62.07% slower | 🔳 *sortBy* | 🚀 Fastest | 59.7% slower | 87.81% slower *split* | 🚀 Fastest | 85% slower | 32.38% slower *splitEvery* | 🚀 Fastest | 89.27% slower | 🔳 *take* | 92.41% slower | 97.76% slower | 🚀 Fastest *takeLast* | 92.71% slower | 98.79% slower | 🚀 Fastest *test* | 🚀 Fastest | 93.95% slower | 🔳 *type* | 38.93% slower | 🚀 Fastest | 🔳 *uniq* | 99.25% slower | 96.57% slower | 🚀 Fastest *update* | 🚀 Fastest | 86.54% slower | 🔳 *view* | 🚀 Fastest | 77.47% slower | 🔳 </details> ## Used by - [WatermelonDB](https://github.com/Nozbe/WatermelonDB) - [SAP's Cloud SDK](https://github.com/SAP/cloud-sdk) - [VSCode Slack intergration](https://github.com/verydanny/vcslack) - [Webpack PostCSS](https://github.com/sectsect/webpack-postcss) - [MobX-State-Tree decorators](https://github.com/farwayer/mst-decorators) - [Mobx decorators](https://github.com/farwayer/mobx-decorators) ## API ### add ```typescript add(a: number, b: number): number ``` It adds `a` and `b`. ```javascript R.add(2, 3) // => 5 ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.add(2%2C%203)%20%2F%2F%20%3D%3E%20%205">Try the above <strong>R.add</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript add(a: number, b: number): number; add(a: number): (b: number) => number; ``` </details> <details> <summary><strong>R.add</strong> source</summary> ```javascript export function add(a, b){ if (arguments.length === 1) return _b => add(a, _b) return Number(a) + Number(b) } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript 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) }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript import {add} from 'rambda' describe('add', () => { it('number', () => { const result = [ add(4)(1), add(4,1) ] result[0] // $ExpectType number result[1] // $ExpectType number }) }) ``` </details> ### adjust ```typescript adjust<T>(index: number, replaceFn: (a: T) => T, list: ReadonlyArray<T>): T[] ``` It replaces `index` in array `list` with the result of `replaceFn(list[i])`. ```javascript R.adjust( 0, a => a + 1, [0, 100] ) // => [1, 100] ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.adjust(%0A%20%200%2C%0A%20%20a%20%3D%3E%20a%20%2B%201%2C%0A%20%20%5B0%2C%20100%5D%0A)%20%2F%2F%20%3D%3E%20%5B1%2C%20100%5D">Try the above <strong>R.adjust</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript adjust<T>(index: number, replaceFn: (a: T) => T, list: ReadonlyArray<T>): T[]; adjust<T>(index: number, replaceFn: (a: T) => T): (list: ReadonlyArray<T>) => T[]; ``` </details> <details> <summary><strong>R.adjust</strong> source</summary> ```javascript import { curry } from './curry' function adjustFn( index, replaceFn, list ){ const actualIndex = index < 0 ? list.length + index : index if (index >= list.length || actualIndex < 0) return list const clone = list.slice() clone[ actualIndex ] = replaceFn(clone[ actualIndex ]) return clone } export const adjust = curry(adjustFn) ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { add } from './add' import { adjust } from './adjust' const expected = [ 0, 11, 2 ] test('without curring', () => { expect(adjust( 1, add(10), [ 0, 1, 2 ] )).toEqual(expected) }) test('with curring type 1 1 1', () => { expect(adjust(1)(add(10))([ 0, 1, 2 ])).toEqual(expected) }) test('with curring type 1 2', () => { expect(adjust(1)(add(10), [ 0, 1, 2 ])).toEqual(expected) }) test('with curring type 2 1', () => { expect(adjust(1, add(10))([ 0, 1, 2 ])).toEqual(expected) }) test('with negative index', () => { expect(adjust( -2, add(10), [ 0, 1, 2 ] )).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) }) ``` </details> <details> <summary>1 failed <italic>Ramda.adjust</italic> specs > Reason for the failure: ramda accepts an array-like object </summary> ```javascript var R = require('../../../../dist/rambda.js'); var eq = require('./shared/eq'); describe('adjust', function() { it('accepts an array-like object', function() { function args() { return arguments; } eq(R.adjust(2, R.add(1), args(0, 1, 2, 3)), [0, 1, 3, 3]); }); }); ``` </details> ### all ```typescript all<T>(predicate: (x: T) => boolean, list: ReadonlyArray<T>): boolean ``` It returns `true`, if all members of array `list` returns `true`, when applied as argument to `predicate` function. ```javascript const list = [ 0, 1, 2, 3, 4 ] const predicate = x => x > -1 const result = R.all(predicate, arr) // => true ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20list%20%3D%20%5B%200%2C%201%2C%202%2C%203%2C%204%20%5D%0Aconst%20predicate%20%3D%20x%20%3D%3E%20x%20%3E%20-1%0A%0Aconst%20result%20%3D%20R.all(predicate%2C%20arr)%0A%2F%2F%20%3D%3E%20true">Try the above <strong>R.all</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript all<T>(predicate: (x: T) => boolean, list: ReadonlyArray<T>): boolean; all<T>(predicate: (x: T) => boolean): (list: ReadonlyArray<T>) => boolean; ``` </details> <details> <summary><strong>R.all</strong> source</summary> ```javascript export function all(predicate, list){ if (arguments.length === 1) return _list => all(predicate, _list) for (let i = 0; i < list.length; i++){ if (!predicate(list[ i ], i)) return false } return true } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { all } from './all' const numArr = [ 0, 1, 2, 3, 4 ] test('when true', () => { const fn = x => x > -1 expect(all(fn)(numArr)).toBeTrue() }) test('when false', () => { const fn = x => x > 2 expect(all(fn, numArr)).toBeFalse() }) test('pass index as second argument', () => { const indexes = [] const fn = (x, i) => { indexes.push(i) return x > 5 } all(fn, [ 10, 12, 14 ]) expect(indexes).toEqual([ 0, 1, 2 ]) }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript import {all} from 'rambda' describe('all', () => { it('happy', () => { const x = all<number>(y => { y // $ExpectType number return y > 0 })([1, 2, 3]) x // $ExpectType boolean const q = all(y => y > 0, [1, 2, 3]) // $ExpectType boolean q // $ExpectType boolean }) }) ``` </details> ### allPass ```typescript allPass<T>(predicates: ((x: T) => boolean)[]): (input: T) => boolean ``` It returns `true`, if all functions of `predicates` return `true`, when `input` is their argument. ```javascript const input = { a : 1, b : 2, } const predicates = [ x => x.a === 1, x => x.b === 2, ] const result = R.allPass(predicates)(input) // => true ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20input%20%3D%20%7B%0A%20%20a%20%3A%201%2C%0A%20%20b%20%3A%202%2C%0A%7D%0Aconst%20predicates%20%3D%20%5B%0A%20%20x%20%3D%3E%20x.a%20%3D%3D%3D%201%2C%0A%20%20x%20%3D%3E%20x.b%20%3D%3D%3D%202%2C%0A%5D%0Aconst%20result%20%3D%20R.allPass(predicates)(input)%20%2F%2F%20%3D%3E%20true">Try the above <strong>R.allPass</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript allPass<T>(predicates: ((x: T) => boolean)[]): (input: T) => boolean; ``` </details> <details> <summary><strong>R.allPass</strong> source</summary> ```javascript export function allPass(predicates){ return input => { let counter = 0 while (counter < predicates.length){ if (!predicates[ counter ](input)){ return false } counter++ } return true } } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { allPass } from './allPass' 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() }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript 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 }) }) ``` </details> <details> <summary>1 failed <italic>Ramda.allPass</italic> specs > Reason for the failure: ramda returns a curried function whose arity matches that of the highest-arity predicate </summary> ```javascript var R = require('../../../../dist/rambda.js'); var eq = require('./shared/eq'); describe('allPass', function() { var odd = function(n) { return n % 2 !== 0; }; var lt20 = function(n) { return n < 20; }; var gt5 = function(n) { return n > 5; }; var plusEq = function(w, x, y, z) { return w + x === y + z; }; it('returns a curried function whose arity matches that of the highest-arity predicate', function() { eq(R.allPass([odd, gt5, plusEq]).length, 4); eq(R.allPass([odd, gt5, plusEq])(9, 9, 9, 9), true); eq(R.allPass([odd, gt5, plusEq])(9)(9)(9)(9), true); }); }); ``` </details> ### always ```typescript always<T>(x: T): () => T ``` It returns function that always returns `x`. ```javascript const fn = R.always(7) console.log(fn())// => 7 ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20fn%20%3D%20R.always(7)%0A%0Aconsole.log(fn())%2F%2F%20%3D%3E%207">Try the above <strong>R.always</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript always<T>(x: T): () => T; ``` </details> <details> <summary><strong>R.always</strong> source</summary> ```javascript export function always(x){ return () => x } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { always } from './always' test('happy', () => { const fn = always(7) expect(fn()).toEqual(7) expect(fn()).toEqual(7) }) ``` </details> ### and ```typescript and<T extends { and?: ((...a: readonly any[]) => any) ``` Returns `true` if both arguments are `true`. Otherwise, it returns `false`. ```javascript R.and(true, true); // => true R.and(false, true); // => false ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.and(true%2C%20true)%3B%20%2F%2F%20%3D%3E%20true%0AR.and(false%2C%20true)%3B%20%2F%2F%20%3D%3E%20false">Try the above <strong>R.and</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript and<T extends { and?: ((...a: readonly any[]) => any); } | number | boolean | string | null>(fn1: T, val2: any): boolean; and<T extends { and?: ((...a: readonly any[]) => any); } | number | boolean | string | null>(fn1: T): (val2: any) => boolean; ``` </details> <details> <summary><strong>R.and</strong> source</summary> ```javascript export function and(a, b){ if (arguments.length === 1) return _b => and(a, _b) return a && b } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { and } from './and' test('happy', () => { expect(and(true, true)).toBe(true) expect(and(true, false)).toBe(false) expect(and(false, true)).toBe(false) expect(and(false, false)).toBe(false) }) ``` </details> ### any ```typescript any<T>(predicate: (x: T, i: number) => boolean, list: ReadonlyArray<T>): boolean ``` It returns `true`, if at least one member of `list` returns true, when passed to `predicate` function. ```javascript const list = [1, 2, 3] const predicate = x => x * x > 8 R.any(fn, list) // => true ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20const%20list%20%3D%20%5B1%2C%202%2C%203%5D%0Aconst%20predicate%20%3D%20x%20%3D%3E%20x%20*%20x%20%3E%208%0AR.any(fn%2C%20list)%0A%2F%2F%20%3D%3E%20true">Try the above <strong>R.any</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript any<T>(predicate: (x: T, i: number) => boolean, list: ReadonlyArray<T>): boolean; any<T>(predicate: (x: T) => boolean, list: ReadonlyArray<T>): boolean; any<T>(predicate: (x: T, i: number) => boolean): (list: ReadonlyArray<T>) => boolean; any<T>(predicate: (x: T) => boolean): (list: ReadonlyArray<T>) => boolean; ``` </details> <details> <summary><strong>R.any</strong> source</summary> ```javascript export function any(predicate, list){ if (arguments.length === 1) return _list => any(predicate, _list) let counter = 0 while (counter < list.length){ if (predicate(list[ counter ], counter)){ return true } counter++ } return false } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { any } from './any' const arr = [ 1, 2 ] test('no curry', () => { expect(any(val => val < 0, arr)).toBeFalse() }) test('with curry', () => { expect(any(val => val < 2)(arr)).toBeTrue() }) test('passes index to predicate', () => { any((x, i) => { expect(typeof x).toBe('string') expect(typeof i).toBe('number') })([ 'foo', 'bar' ]) }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript import {any} from 'rambda' describe('any', () => { it('1', () => { const x = any<number>( (y, i) => { y // $ExpectType number i // $ExpectType number return y > 2 }, [1, 2, 3] ) x // $ExpectType boolean }) it('2', () => { const x = any<number>( y => { y // $ExpectType number return y > 2 }, [1, 2, 3] ) x // $ExpectType boolean }) it('1 curry', () => { const x = any<number>((y, i) => { y // $ExpectType number i // $ExpectType number return y > 2 })([1, 2, 3]) x // $ExpectType boolean }) it('2 curry', () => { const x = any<number>(y => { y // $ExpectType number return y > 2 })([1, 2, 3]) x // $ExpectType boolean }) }) ``` </details> ### anyPass ```typescript anyPass<T>(predicates: ReadonlyArray<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`. ```javascript const isBig = x => x > 20 const isOdd = x => x % 2 === 1 const input = 11 const fn = R.anyPass( [isBig, isOdd] ) const result = fn(input) // => true ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20isBig%20%3D%20x%20%3D%3E%20x%20%3E%2020%0Aconst%20isOdd%20%3D%20x%20%3D%3E%20x%20%25%202%20%3D%3D%3D%201%0Aconst%20input%20%3D%2011%0A%0Aconst%20fn%20%3D%20R.anyPass(%0A%20%20%5BisBig%2C%20isOdd%5D%0A)%0A%0Aconst%20result%20%3D%20fn(input)%20%0A%2F%2F%20%3D%3E%20true">Try the above <strong>R.anyPass</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript anyPass<T>(predicates: ReadonlyArray<SafePred<T>>): SafePred<T>; ``` </details> <details> <summary><strong>R.anyPass</strong> source</summary> ```javascript export function anyPass(predicates){ return input => { let counter = 0 while (counter < predicates.length){ if (predicates[ counter ](input)){ return true } counter++ } return false } } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { anyPass } from './anyPass' 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) }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript 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 }) }) ``` </details> <details> <summary>1 failed <italic>Ramda.anyPass</italic> specs > Reason for the failure: ramda returns a curried function whose arity matches that of the highest-arity predicate </summary> ```javascript var R = require('../../../../dist/rambda.js'); var eq = require('./shared/eq'); describe('anyPass', function() { var odd = function(n) { return n % 2 !== 0; }; var gt20 = function(n) { return n > 20; }; var lt5 = function(n) { return n < 5; }; var plusEq = function(w, x, y, z) { return w + x === y + z; }; it('returns a curried function whose arity matches that of the highest-arity predicate', function() { eq(R.anyPass([odd, lt5, plusEq]).length, 4); eq(R.anyPass([odd, lt5, plusEq])(6, 7, 8, 9), false); eq(R.anyPass([odd, lt5, plusEq])(6)(7)(8)(9), false); }); }); ``` </details> ### append ```typescript append<T>(x: T, listOrString: ReadonlyArray<T>): T[] ``` It adds element `x` at the end of `listOrString`. ```javascript const x = 'foo' const result = [ R.append(x, 'cherry_'), R.append(x, ['bar', 'baz']) ] // => ['cherry_foo', ['bar', 'baz', 'foo']] ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20x%20%3D%20'foo'%0A%0Aconst%20result%20%3D%20%5B%0A%20%20R.append(x%2C%20'cherry_')%2C%0A%20%20R.append(x%2C%20%5B'bar'%2C%20'baz'%5D)%0A%5D%0A%2F%2F%20%3D%3E%20%5B'cherry_foo'%2C%20%5B'bar'%2C%20'baz'%2C%20'foo'%5D%5D">Try the above <strong>R.append</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript append<T>(x: T, listOrString: ReadonlyArray<T>): T[]; append<T>(x: T): <T>(listOrString: ReadonlyArray<T>) => T[]; ``` </details> <details> <summary><strong>R.append</strong> source</summary> ```javascript export function append(x, listOrString){ if (arguments.length === 1) return _listOrString => append(x, _listOrString) if (typeof listOrString === 'string') return `${ listOrString }${ x }` const clone = listOrString.slice() clone.push(x) return clone } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { append } from './append' import { compose } from './compose.js' import { flatten } from './flatten.js' import { map } from './map' test('with strings', () => { expect(append('o', 'fo')).toEqual('foo') }) test('with arrays', () => { expect(append('tests', [ 'write', 'more' ])).toEqual([ 'write', 'more', 'tests', ]) }) test('append to empty array', () => { expect(append('tests', [])).toEqual([ 'tests' ]) }) test('happy', () => { const result = compose(flatten, map(append(0)))([ [ 1 ], [ 2 ], [ 3 ] ]) expect(result).toEqual([ 1, 0, 2, 0, 3, 0 ]) }) test('should not modify arguments', () => { const a = [ 1, 2, 3 ] const b = append(4, a) expect(a).toEqual([ 1, 2, 3 ]) expect(b).toEqual([ 1, 2, 3, 4 ]) }) ``` </details> ### applySpec ```typescript applySpec<Spec extends Record<string, (...args: readonly any[]) => any>>( spec: Spec ): ( ...args: Parameters<ValueOfRecord<Spec>> ) => { [Key in keyof Spec]: ReturnType<Spec[Key]> } ``` It returns a curried function with the same arity as the longest function in the spec object. Arguments will be applied to the spec methods recursively. ```javascript const spec = { name: R.path('deeply.nested.firstname') } const json = { deeply: { nested: { firstname: 'barry' } } } const result = R.applySpec(spec, json) // => { name: 'barry' } // Second example const getMetrics = R.applySpec({ sum: R.add, nested: { mul: R.multiply } }); getMetrics(2, 4); // => { sum: 6, nested: { mul: 8 } } ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20spec%20%3D%20%7B%0A%20%20name%3A%20R.path('deeply.nested.firstname')%0A%7D%0Aconst%20json%20%3D%20%7B%0A%20%20deeply%3A%20%7B%0A%20%20%20nested%3A%20%7B%0A%20%20%20%20%20%20firstname%3A%20'barry'%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0Aconst%20result%20%3D%20R.applySpec(spec%2C%20json)%20%2F%2F%20%3D%3E%20%7B%20name%3A%20'barry'%20%7D%0A%0A%2F%2F%20Second%20example%0Aconst%20getMetrics%20%3D%20R.applySpec(%7B%0A%20%20sum%3A%20R.add%2C%0A%20%20nested%3A%20%7B%20mul%3A%20R.multiply%20%7D%0A%7D)%3B%0AgetMetrics(2%2C%204)%3B%20%2F%2F%20%3D%3E%20%7B%20sum%3A%206%2C%20nested%3A%20%7B%20mul%3A%208%20%7D%20%7D">Try the above <strong>R.applySpec</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript applySpec<Spec extends Record<string, (...args: readonly any[]) => any>>( spec: Spec ): ( ...args: Parameters<ValueOfRecord<Spec>> ) => { [Key in keyof Spec]: ReturnType<Spec[Key]> }; applySpec<T>(spec: any): (...args: readonly any[]) => T; ``` </details> <details> <summary><strong>R.applySpec</strong> source</summary> ```javascript // 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 (Array.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' || Array.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 } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript 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.skip('retains the highest arity', () => { const f = applySpec({ f1 : nAry(2, T), f2 : nAry(5, T), }) expect(f.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', }) }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript import {multiply, applySpec, inc, dec, add} from 'rambda' describe('applySpec', () => { it('ramda 1', () => { const result = applySpec({ v: inc, u: dec, })(1) result // $ExpectType { v: number; u: number; } }) it('ramda 1', () => { interface Output { sum: number, multiplied: number, } const result = applySpec<Output>({ sum: add, multiplied: multiply, })(1, 2) result // $ExpectType Output }) }) ``` </details> ### assoc ```typescript assoc<T, U, K extends string>(prop: K, newValue: T, obj: U): Record<K, T> & U ``` It makes a shallow clone of `obj` with setting or overriding the property `prop` with `newValue`. ```javascript R.assoc('c', 3, {a: 1, b: 2}) //=> {a: 1, b: 2, c: 3} ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20R.assoc('c'%2C%203%2C%20%7Ba%3A%201%2C%20b%3A%202%7D)%0A%2F%2F%3D%3E%20%7Ba%3A%201%2C%20b%3A%202%2C%20c%3A%203%7D">Try the above <strong>R.assoc</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript assoc<T, U, K extends string>(prop: K, newValue: T, obj: U): Record<K, T> & U; assoc<T, K extends string>(prop: K, newValue: T): <U>(obj: U) => Record<K, T> & U; assoc<K extends string>(prop: K): <T, U>(newValue: T, obj: U) => Record<K, T> & U; ``` </details> <details> <summary><strong>R.assoc</strong> source</summary> ```javascript import { curry } from './curry' function assocFn( prop, newValue, obj ){ return Object.assign( {}, obj, { [ prop ] : newValue } ) } export const assoc = curry(assocFn) ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript 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 } }) }) ``` </details> ### assocPath ```typescript assocPath<T, U>(path: Path, newValue: T, obj: U): U ``` It makes a shallow clone of `obj` with setting or overriding with `newValue` the property found with `path`. ```javascript const path = 'b.c' const newValue = 2 const obj = { a: 1 } R.assocPath(path, newValue, obj) // => { a : 1, b : { c : 2 }} ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20result%20%3D%20const%20path%20%3D%20'b.c'%0Aconst%20newValue%20%3D%202%0Aconst%20obj%20%3D%20%7B%20a%3A%201%20%7D%0A%0AR.assocPath(path%2C%20newValue%2C%20obj)%0A%2F%2F%20%3D%3E%20%7B%20a%20%3A%201%2C%20b%20%3A%20%7B%20c%20%3A%202%20%7D%7D">Try the above <strong>R.assocPath</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript assocPath<T, U>(path: Path, newValue: T, obj: U): U; assocPath<T, U>(path: Path, newValue: T): (obj: U) => U; assocPath<T, U>(path: Path): FToolbelt.Curry<(a: T, b: U) => U>; ``` </details> <details> <summary><strong>R.assocPath</strong> source</summary> ```javascript import { _isInteger } from './_internals/_isInteger' import { assoc } from './assoc' import { curry } from './curry' function assocPathFn( list, newValue, input ){ const pathArrValue = typeof list === 'string' ? list.split('.') : list 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(parseInt(pathArrValue[ 1 ], 10)) ? [] : {} : input[ index ] newValue = assocPathFn( Array.prototype.slice.call(pathArrValue, 1), newValue, nextinput ) } if (_isInteger(parseInt(index, 10)) && Array.isArray(input)){ const arr = input.slice() arr[ index ] = newValue return arr } return assoc( index, newValue, input ) } export const assocPath = curry(assocPathFn) ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript import { assocPath } from './assocPath' 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 nested array to a non-empty object - curry case 1', () => { expect(assocPath('b.0', 2)({ a : 1 })).toEqual({ a : 1, b : [ 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('happy', () => { 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) }) ``` </details> ### both ```typescript both(pred1: Pred, pred2: Pred): Pred ``` It returns a function with `input` argument. This function will return `true`, if both `firstCondition` and `secondCondition` return `true` when `input` is passed as their argument. ```javascript const firstCondition = x => x > 10 const secondCondition = x => x < 20 const fn = R.both(secondCondition) const result = [fn(15), fn(30)] // => [true, false] ``` <a title="redirect to Rambda Repl site" href="https://rambda.now.sh?const%20firstCondition%20%3D%20x%20%3D%3E%20x%20%3E%2010%0Aconst%20secondCondition%20%3D%20x%20%3D%3E%20x%20%3C%2020%0Aconst%20fn%20%3D%20R.both(secondCondition)%0A%0Aconst%20result%20%3D%20%5Bfn(15)%2C%20fn(30)%5D%0A%2F%2F%20%3D%3E%20%5Btrue%2C%20false%5D">Try the above <strong>R.both</strong> example in Rambda REPL</a> <details> <summary>All Typescript definitions</summary> ```typescript 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; ``` </details> <details> <summary><strong>R.both</strong> source</summary> ```javascript export function both(f, g){ if (arguments.length === 1) return _g => both(f, _g) return (...input) => f(...input) && g(...input) } ``` </details> <details> <summary><strong>Tests</strong></summary> ```javascript 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') }) ``` </details> <details> <summary><strong>Typescript</strong> test</summary> ```typescript import {both} from 'rambda' describe('both', () => { it('with passed type', () => { const fn = both<number>( // $ExpectType Predicate<number> x => { return x > 1 }, x => { return x % 2 === 0 } ) const result = fn(2) // $ExpectType boolean result // $ExpectType boolean }) it('no type passed', () => { const fn = both( x => { x // $ExpectType any return x > 1 }, x => { return x % 2 === 0 } ) const result = fn(2) // $ExpectType boolean result // $ExpectType boolean }) }) describe('both + curry', () => { it('with passed type', () => { const fn = both<number>(x => { return x > 1 })(x => { return x % 2 === 0 }) fn // $ExpectType Predicate<number> const result = fn(2) // $ExpectType boolean result // $ExpectType boolean }) it('no type passed', () => { const fn = both(x => { x // $ExpectType unknown return (x as number) > 1 })(x => { return (x as number) % 2 === 0 }) const result = fn(2) // $ExpectType boolean result // $ExpectType boolean }) }) ``` </details> <details> <summary>1 failed <italic>Ramda.both</italic> specs > Reason for the failure: ramda supports fantasy-land </summary> ```javascript var S = require('sanctuary'); var R = require('../../../../dist/rambda.js'); var eq = require('./shared/eq'); describe('both', function() { it('accepts fantasy-land applicative functors', function() { var Just = S.Jus