UNPKG

rambda

Version:

Lightweight and faster alternative to Ramda with included TS definitions

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