Package detail

rambda

selfrefactor8.4mMIT10.1.0

Lightweight and faster alternative to Ramda with included TS definitions

curried, fp, functional, generics

readme

Rambda

Rambda is TypeScript-focused utility library similar to Remeda, Ramda and Radashi.

Initially it started as faster alternative to functional programming library Ramda, but in order to address many TypeScript issues, now Rambda takes a separate path. - Documentation

Commit activity Library size install size PR's Welcome GitHub contributors

❯ Example use

import { pipe, map, filter } from 'rambda'

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

You can test this example in Rambda's REPL

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

❯ 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:

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);
});

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

Fast performance compared to Ramda

Since Rambda methods doesn't use so many internals, it is faster than Ramda. Prior to version 10, benchmark summary was included, but now the main selling point is the TypeScript focus, not performance so this is no longer included.

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.

Documentation site of Rambda version 9.4.2 is available here.

From version 10.0.0 onwards, Rambda will start to diverge from Ramda in order to address some of the issues that Ramda has.

Currently, Rambda includes 32 methods that differ from Ramda and shares 83 methods with it.

<summary> Ramda issues </summary> -- Typescript support - this is the main reason for the divergence. Most of design decisions in Rambda are made with Typescript in mind. -- Methods that imply side-effect, which is not FP oriented, e.g. R.forEach. -- Naming of methods that doesn't match developer's expectation, such as R.chain, which should be called flatMap. -- Naming of methods is sometimes too generic to be remembered such as R.update, R.modify, R.where. -- Methods that are already present in standard JavaScript, such as R.toLower, R.length. -- R.compose doesn't have the best possible TypeScript support.

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

API

addProp


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.

const result = R.pipe(
    { a: 1, b: 'foo' }, 
    R.addProp('c', 3)
)
// => { a: 1, b: 'foo', c: 3 }

Try this R.addProp example in Rambda REPL

<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>>;
<summary>R.addProp source</summary> javascript export function addProp(key, value) { return obj => ({ ...obj, [key]: value }) }
<summary>Tests</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) })
<summary>TypeScript 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 })

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

addPropToObjects


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.

const result = R.pipe(
    [
        {a: 1, b: 2},
        {a: 3, b: 4},
    ],
    R.addPropToObjects(
        'c',
        (x) => String(x.a + x.b),
    )
)
// => [{a: 1, b: 2, c: '3'}, {a: 3, b: 4, c: '7'}]

Try this R.addPropToObjects example in Rambda REPL

<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 }>[];
<summary>R.addPropToObjects source</summary> javascript import { mapFn } from './map.js' export function addPropToObjects ( property, fn ){ return listOfObjects => mapFn( (obj) => ({ ...(obj), [property]: fn(obj) }), listOfObjects ) }
<summary>Tests</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' }, ]) })
<summary>TypeScript 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; }[] })

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

all


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

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

const list = [ 0, 1, 2, 3, 4 ]
const predicate = x => x > -1

const result = R.pipe(
    list,
    R.all(predicate)
) // => true

Try this R.all example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript all<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
<summary>R.all 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 } }
<summary>Tests</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() })
<summary>TypeScript 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 }) })

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

allPass


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.

const list = [[1, 2, 3, 4], [3, 4, 5]]
const result = R.pipe(
    list,
    R.filter(R.allPass([R.includes(2), R.includes(3)]))
) // => [[1, 2, 3, 4]]

Try this R.allPass example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript allPass<F extends (...args: any[]) => boolean>(predicates: readonly F[]): F;
<summary>R.allPass 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 } }
<summary>Tests</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([]) })
<summary>TypeScript 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[] }) })

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

any


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

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

const list = [1, 2, 3]
const predicate = x => x * x > 8
R.any(fn)(list)
// => true

Try this R.any example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript any<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
<summary>R.any 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 } }
<summary>Tests</summary> javascript import { any } from './any.js' const list = [1, 2, 3] test('happy', () => { expect(any(x => x > 2)(list)).toBeTruthy() })
<summary>TypeScript 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 })

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

anyPass


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.

const isBig = x => x > 20
const isOdd = x => x % 2 === 1
const input = 11

const fn = R.anyPass(
  [isBig, isOdd]
)

const result = fn(input) 
// => true

Try this R.anyPass example in Rambda REPL

<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; ... ...
<summary>R.anyPass 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 } }
<summary>Tests</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() })
<summary>TypeScript 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 } }) })

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

append


append<T>(el: T): (list: T[]) => T[]

It adds element x at the end of iterable.

const x = 'foo'

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

Try this R.append example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript append<T>(el: T): (list: T[]) => T[]; append<T>(el: T): (list: readonly T[]) => T[];
<summary>R.append source</summary> javascript import { cloneList } from './_internals/cloneList.js' export function append(x) { return list => { const clone = cloneList(list) clone.push(x) return clone } }
<summary>Tests</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']) })
<summary>TypeScript 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; }[] }) })

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

ascend


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.

const result = R.pipe(
    [{a: 1}, {a: 2}, {a: 0}],
    R.sort(R.ascend(R.prop('a')))
)
// => [{a: 0}, {a: 1}, {a: 2}]

Try this R.ascend example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript ascend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
<summary>R.ascend 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) } }
<summary>Tests</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}]) })
<summary>TypeScript 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; }[] })

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

assertType


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.

<summary>All TypeScript definitions</summary> typescript assertType<T, U extends T>(fn: (x: T) => x is U) : (x: T) => U;
<summary>R.assertType source</summary> javascript export function assertType(fn) { return (x) => { if (fn(x)) { return x } throw new Error('type assertion failed in R.assertType') } }
<summary>Tests</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') })
<summary>TypeScript 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 })

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

checkObjectWithSpec


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.

const condition = R.checkObjectWithSpec({
  a : x => typeof x === "string",
  b : x => x === 4
})
const input = {
  a : "foo",
  b : 4,
  c : 11,
}

const result = condition(input) 
// => true

Try this R.checkObjectWithSpec example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript checkObjectWithSpec<T>(spec: T): <U>(testObj: U) => boolean;
<summary>R.checkObjectWithSpec 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 } }
<summary>Tests</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) })
<summary>TypeScript 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 }) })

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

compact


compact<T>(list: T[]): Array<StrictNonNullable<T>>

It removes null and undefined members from list or object input.

const result = R.pipe(
    {
        a: [ undefined, '', 'a', 'b', 'c'],
        b: [1,2, null, 0, undefined, 3],
        c: { a: 1, b: 2, c: 0, d: undefined, e: null, f: false },
    },
    x => ({
        a: R.compact(x.a),
        b: R.compact(x.b),
        c: R.compact(x.c)
    })
)
// => { a: ['a', 'b', 'c'], b: [1, 2, 3], c: { a: 1, b: 2, c: 0, f: false } }

Try this R.compact example in Rambda REPL

<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> };
<summary>R.compact 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) }
<summary>Tests</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 }) })
<summary>TypeScript 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; } })

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

complement


complement<T extends any[]>(predicate: (...args: T) => unknown): (...args: T) => boolean

It returns inverted version of origin function that accept input as argument.

The return value of inverted is the negative boolean value of origin(input).

const fn = x => x > 5
const inverted = complement(fn)

const result = [
  fn(7),
  inverted(7)
] => [ true, false ]

Try this R.complement example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript complement<T extends any[]>(predicate: (...args: T) => unknown): (...args: T) => boolean;
<summary>R.complement source</summary> javascript export function complement(fn) { return (...input) => !fn(...input) }
<summary>Tests</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() })
<summary>TypeScript 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 }) })

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

concat


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

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

R.concat([1, 2])([3, 4]) // => [1, 2, 3, 4]
R.concat('foo')('bar') // => 'foobar'

Try this R.concat example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript concat<T>(x: T[]): (y: T[]) => T[]; concat(x: string): (y: string) => string;
<summary>R.concat source</summary> javascript export function concat(x) { return y => (typeof x === 'string' ? `${x}${y}` : [...x, ...y]) }
<summary>TypeScript 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 })

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

convertToType


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.

<summary>All TypeScript definitions</summary> typescript convertToType<T>(x: unknown) : T;
<summary>R.convertToType source</summary> javascript export function convertToType(x) { return x }
<summary>TypeScript 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[] })

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

count


count<T>(predicate: (x: T) => boolean): (list: T[]) => number

It counts how many times predicate function returns true, when supplied with iteration of list.

const list = [{a: 1}, 1, {a:2}]
const result = R.count(x => x.a !== undefined)(list)
// => 2

Try this R.count example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript count<T>(predicate: (x: T) => boolean): (list: T[]) => number;
<summary>R.count 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 } }
<summary>Tests</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) })
<summary>TypeScript 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 })

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

countBy


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.

const list = [ 'a', 'A', 'b', 'B', 'c', 'C' ]

const result = countBy(x => x.toLowerCase())( list)
const expected = { a: 2, b: 2, c: 2 }
// => `result` is equal to `expected`

Try this R.countBy example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript countBy<T>(fn: (x: T) => string | number): (list: T[]) => { [index: string]: number };
<summary>R.countBy 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 } }
<summary>Tests</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, }) })
<summary>TypeScript 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; } })

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

createObjectFromKeys


createObjectFromKeys<const K extends readonly PropertyKey[], V>(
    fn: (key: K[number]) => V
): (keys: K) => { [P in K[number]]: V }
const result = R.createObjectFromKeys(
    (x, index) => `${x}-${index}`
)(['a', 'b', 'c'])
// => {a: 'a-0', b: 'b-1', c: 'c-2'}

Try this R.createObjectFromKeys example in Rambda REPL

<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 };
<summary>R.createObjectFromKeys source</summary> javascript export function createObjectFromKeys(keys) { return fn => { const result = {} keys.forEach((key, index) => { result[key] = fn(key, index) }) return result } }
<summary>Tests</summary> javascript import { createObjectFromKeys } from './createObjectFromKeys.js' test('happy', () => { const result = createObjectFromKeys(['a', 'b'])((key, index) => key.toUpperCase() + index) const expected = { a: 'A0', b: 'B1' } expect(result).toEqual(expected) })

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

defaultTo


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

R.defaultTo('foo')('bar') // => 'bar'
R.defaultTo('foo'))(undefined) // => 'foo'

// Important - emtpy string is not falsy value
R.defaultTo('foo')('') // => 'foo'

Try this R.defaultTo example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript defaultTo<T>(defaultValue: T): (input: unknown) => T;
<summary>R.defaultTo 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 }
<summary>Tests</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') })
<summary>TypeScript test</summary> typescript import { defaultTo, pipe } from 'rambda' describe('R.defaultTo', () => { it('happy', () => { const result = pipe('bar' as unknown, defaultTo('foo')) result // $ExpectType string }) })

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

descend


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.

const result = R.pipe(
    [{a: 1}, {a: 2}, {a: 0}],
    R.sort(R.descend(R.prop('a')))
)
// => [{a: 2}, {a: 1}, {a: 0}]

Try this R.descend example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript descend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
<summary>R.descend 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) } }

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

drop


drop<T>(howMany: number): (list: T[]) => T[]

It returns howMany items dropped from beginning of list.

R.drop(2)(['foo', 'bar', 'baz']) // => ['baz']

Try this R.drop example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript drop<T>(howMany: number): (list: T[]) => T[];
<summary>R.drop source</summary> javascript export function drop(howManyToDrop, ) { return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0) }
<summary>Tests</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]) })
<summary>TypeScript test</summary> typescript import { drop, pipe } from 'rambda' it('R.drop', () => { const result = pipe([1, 2, 3, 4], drop(2)) result // $ExpectType number[] })

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

dropLast


dropLast<T>(howMany: number): (list: T[]) => T[]

It returns howMany items dropped from the end of list.

<summary>All TypeScript definitions</summary> typescript dropLast<T>(howMany: number): (list: T[]) => T[];
<summary>R.dropLast source</summary> javascript export function dropLast(numberItems) { return list => (numberItems > 0 ? list.slice(0, -numberItems) : list.slice()) }
<summary>Tests</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]) })

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

dropLastWhile


dropLastWhile<T>(predicate: (x: T, index: number) => boolean): (list: T[]) => T[]
const list = [1, 2, 3, 4, 5];
const predicate = x => x >= 3

const result = dropLastWhile(predicate)(list);
// => [1, 2]

Try this R.dropLastWhile example in Rambda REPL

<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[];
<summary>R.dropLastWhile 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 (!predicate(item, counter)) { toReturn.push(item) break } } while (counter) { toReturn.push(list[--counter]) } return toReturn.reverse() } }
<summary>Tests</summary> javascript import { dropLastWhile } from './dropLastWhile.js' const list = [1, 2, 3, 4, 5] test('with list', () => { const result = dropLastWhile(x => x >= 3)(list) expect(result).toEqual([1, 2]) }) test('with empty list', () => { expect(dropLastWhile(() => true)([])).toEqual([]) })

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

dropRepeatsBy


dropRepeatsBy<T, U>(fn: (x: T) => U): (list: T[]) => T[]
const result = R.dropRepeatsBy(
  Math.abs,
  [1, -1, 2, 3, -3]
)
// => [1, 2, 3]

Try this R.dropRepeatsBy example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript dropRepeatsBy<T, U>(fn: (x: T) => U): (list: T[]) => T[];

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

dropRepeatsWith


dropRepeatsWith<T>(predicate: (x: T, y: T) => boolean): (list: T[]) => T[]
const list = [{a:1,b:2}, {a:1,b:3}, {a:2, b:4}]
const result = R.dropRepeatsWith(R.prop('a'))(list)

// => [{a:1,b:2}, {a:2, b:4}]

Try this R.dropRepeatsWith example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript dropRepeatsWith<T>(predicate: (x: T, y: T) => boolean): (list: T[]) => T[];

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

dropWhile


dropWhile<T>(predicate: (x: T, index: number) => boolean): (list: T[]) => T[]
const list = [1, 2, 3, 4]
const predicate = x => x < 3
const result = R.dropWhile(predicate)(list)
// => [3, 4]

Try this R.dropWhile example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript dropWhile<T>(predicate: (x: T, index: number) => boolean): (list: T[]) => T[]; dropWhile<T>(predicate: (x: T) => boolean): (list: T[]) => T[];
<summary>R.dropWhile source</summary> javascript export function dropWhile(predicate) { return iterable => { const toReturn = [] let counter = 0 while (counter < iterable.length) { const item = iterable[counter++] if (!predicate(item, counter)) { toReturn.push(item) break } } while (counter < iterable.length) { toReturn.push(iterable[counter++]) } return toReturn } }
<summary>Tests</summary> javascript import { dropWhile } from './dropWhile.js' const list = [1, 2, 3, 4] test('happy', () => { const predicate = (x, i) => { expect(typeof i).toBe('number') return x < 3 } const result = dropWhile(predicate)(list) expect(result).toEqual([3, 4]) }) test('always false', () => { const predicate = () => 0 const result = dropWhile(predicate)(list) expect(result).toEqual(list) })
<summary>TypeScript test</summary> typescript import { dropWhile, pipe } from 'rambda' const list = [1, 2, 3] describe('R.dropWhile', () => { it('happy', () => { const result = pipe( list, dropWhile(x => x > 1), ) result // $ExpectType number[] }) it('with index', () => { const result = pipe( list, dropWhile((x, i) => { i // $ExpectType number return x + i > 2 }), ) result // $ExpectType number[] }) })

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

eqBy


eqBy<T>(fn: (x: T) => unknown, a: T): (b: T) => boolean
const result = R.eqBy(Math.abs, 5)(-5)
// => true

Try this R.eqBy example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript eqBy<T>(fn: (x: T) => unknown, a: T): (b: T) => boolean;
<summary>R.eqBy source</summary> javascript import { equalsFn } from './equals.js' export function eqBy(fn, a) { return b => equalsFn(fn(a), fn(b)) }
<summary>Tests</summary> javascript import { eqBy } from './eqBy.js' test('deteremines whether two values map to the same value in the codomain', () => { expect(eqBy(Math.abs, 5)(5)).toBe(true) expect(eqBy(Math.abs, 5)(-5)).toBe(true) expect(eqBy(Math.abs, -5)(5)).toBe(true) expect(eqBy(Math.abs, -5)(-5)).toBe(true) expect(eqBy(Math.abs, 42)(99)).toBe(false) }) test('has R.equals semantics', () => { expect(eqBy(Math.abs, Number.NaN)(Number.NaN)).toBe(true) expect(eqBy(Math.abs, [42])([42])).toBe(true) expect(eqBy(x => x, { a: 1 })({ a: 1 })).toBe(true) expect(eqBy(x => x, { a: 1 })({ a: 2 })).toBe(false) })

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

eqProps


eqProps<T, K extends keyof T>(prop: K, obj1: T): (obj2: T) => boolean

It returns true if property prop in obj1 is equal to property prop in obj2 according to R.equals.

const obj1 = {a: 1, b:2}
const obj2 = {a: 1, b:3}
const result = R.eqProps('a', obj1)(obj2)
// => true

Try this R.eqProps example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript eqProps<T, K extends keyof T>(prop: K, obj1: T): (obj2: T) => boolean;
<summary>R.eqProps source</summary> javascript import { equalsFn } from './equals.js' export function eqProps(property, objA) { return objB => equalsFn( objA[property], objB[property] ) }
<summary>Tests</summary> javascript import { eqProps } from './eqProps.js' const obj1 = { a: 1, b: 2, } const obj2 = { a: 1, b: 3, } test('props are equal', () => { const result = eqProps('a', obj1)(obj2) expect(result).toBeTruthy() }) test('props are not equal', () => { const result = eqProps('b', obj1)(obj2) expect(result).toBeFalsy() }) test('prop does not exist', () => { const result = eqProps('c', obj1)(obj2) expect(result).toBeTruthy() })
<summary>TypeScript test</summary> typescript import { eqProps, pipe } from 'rambda' const obj1 = { a: { b: 1 }, c: 2 } const obj2 = { a: { b: 1 }, c: 3 } it('R.eqProps', () => { const result = pipe(obj1, eqProps('a', obj2)) result // $ExpectType boolean })

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

equals


equals<T>(x: T, y: T): boolean

It deeply compares x and y and returns true if they are equal.

:boom: It doesn't handle cyclical data structures and functions

R.equals(
  [1, {a:2}, [{b: 3}]],
  [1, {a:2}, [{b: 3}]]
) // => true

Try this R.equals example in Rambda REPL

<summary>All TypeScript definitions</summary> typescript equals<T>(x: T, y: T): boolean; equals<T>(x: T): (y: T) => boolean;
<summary>R.equals source</summary> javascript import { isArray } from './_internals/isArray.js' import { type } from './type.js' export function _lastIndexOf(valueToFind, list) { if (!isArray(list)) { throw new Error(`Cannot read property 'indexOf' of ${list}`) } const typeOfValue = type(valueToFind) if (!['Array', 'NaN', 'Object', 'RegExp'].includes(typeOfValue)) { return list.lastIndexOf(valueToFind) } const { length } = list let index = length let foundIndex = -1 while (--index > -1 && foundIndex === -1) { if (equalsFn(list[index], valueToFind)) { foundIndex = index } } return foundIndex } export function _indexOf(valueToFind, list) { if (!isArray(list)) { throw new Error(`Cannot read property 'indexOf' of ${list}`) } const typeOfValue = type(valueToFind) if (!['Array', 'NaN', 'Object', 'RegExp'].includes(typeOfValue)) { return list.indexOf(valueToFind) } let index = -1 let foundIndex = -1 const { length } = list while (++index < length && foundIndex === -1) { if (equalsFn(list[index], valueToFind)) { foundIndex = index } } return foundIndex } function _arrayFromIterator(iter) { const list = [] let next while (!(next = iter.next()).done) { list.push(next.value) } return list } function _compareSets(a, b) { if (a.size !== b.size) { return false } const aList = _arrayFromIterator(a.values()) const bList = _arrayFromIterator(b.values()) const filtered = aList.filter(aInstance => _indexOf(aInstance, bList) === -1) return filtered.length === 0 } function compareErrors(a, b) { if (a.message !== b.message) { return false } if (a.toString !== b.toString) { return false } return a.toString() === b.toString() } function parseDate(maybeDate) { if (!maybeDate.toDateString) { return [false] } return [true, maybeDate.getTime()] } function parseRegex(maybeRegex) { if (maybeRegex.constructor !== RegExp) { return [false] } return [true, maybeRegex.toString()] } export function equalsFn(a, b) { if (Object.is(a, b)) { return true } const aType = type(a) if (aType !== type(b)) { return false } if (aType === 'Function') { return a.name === undefined ? false : a.name === b.name } if (['NaN', 'Null', 'Undefined'].includes(aType)) { return true } if (['BigInt', 'Number'].includes(aType)) { if (Object.is(-0, a) !== Object.is(-0, b)) { return false } return a.toString() === b.toString() } if (['Boolean', 'String'].includes(aType)) { return a.toString() === b.toString() } if (aType === 'Array') { const aClone = Array.from(a) const bClone = Array.from(b) if (aClone.toString() !== bClone.toString()) { return false } let loopArrayFlag = true aClone.forEach((aCloneInstance, aCloneIndex) => { if (loopArrayFlag) { if ( aCloneInstance !== bClone[aCloneIndex] && !equalsFn(aCloneInstance, bClone[aCloneIndex]) ) { loopArrayFlag = false } } }) return loopArrayFlag } const aRegex = parseRegex(a) const bRegex = parseRegex(b) if (aRegex[0]) { return bRegex[0] ? aRegex[1] === bRegex[1] : false } if (bRegex[0]) { return false } const aDate = parseDate(a) const bDate = parseDate(b) if (aDate[0]) { return bDate[0] ? aDate[1] === bDate[1] : false } if (bDate[0]) { return false } if (a instanceof Error) { if (!(b instanceof Error)) { return false } return compareErrors(a, b) } if (aType === 'Set') { return _compareSets(a, b) } if (aType === 'Object') { const aKeys = Object.keys(a) if (aKeys.length !== Object.keys(b).length) { return false } let loopObjectFlag = true aKeys.forEach(aKeyInstance => { if (loopObjectFlag) { const aValue = a[aKeyInstance] const bValue = b[aKeyInstance] if (aValue !== bValue && !equalsFn(aValue, bValue)) { loopObjectFlag = false } } }) return loopObjectFlag } return false } export function equals(a) { return b => equalsFn(a, b) }
<summary>Tests</summary>

```javascript import { equalsFn } from './equals.js'

test('compare functions', () => { function foo() {} function bar() {} const baz = () => {}

const expectTrue = equalsFn(foo, foo) const expectFalseFirst = equalsFn(foo, bar) const expectFalseSecond = equalsFn(foo, baz)

expect(expectTrue).toBeTruthy() expect(expectFalseFirst).toBeFalsy() expect(expectFalseSecond).toBeFalsy() })

test('with array of objects', () => { const list1 = [{ a: 1 }, [{ b: 2 }]] const list2 = [{ a: 1 }, [{ b: 2 }]] const list3 = [{ a: 1 }, [{ b: 3 }]]

expect(equalsFn(list1, list2)).toBeTruthy() expect(equalsFn(list1, list3)).toBeFalsy() })

test('with regex', () => { expect(equalsFn(/s/, /s/)).toBeTruthy() expect(equalsFn(/s/, /d/)).toBeFalsy() expect(equalsFn(/a/gi, /a/gi)).toBeTruthy() expect(equalsFn(/a/gim, /a/gim)).toBeTruthy() expect(equalsFn(/a/gi, /a/i)).toBeFalsy() })

test('not a number', () => { expect(equalsFn([Number.NaN], [Number.NaN])).toBeTruthy() })

test('new number', () => { expect(equalsFn(new Number(0), new Number(0))).toBeTruthy() expect(equalsFn(new Number(0), new Number(1))).toBeFalsy() expect(equalsFn(new Number(1), new Number(0))).toBeFalsy() })

test('new string', () => { expect(equalsFn(new String(''), new String(''))).toBeTruthy() expect(equalsFn(new String(''), new String('x'))).toBeFalsy() expect(equalsFn(new String('x'), new String(''))).toBeFalsy() expect(equalsFn(new String('foo'), new String('foo'))).toBeTruthy() expect(equalsFn(new String('foo'), new String('bar'))).toBeFalsy() expect(equalsFn(new String('bar'), new String('foo'))).toBeFalsy() })

test('new Boolean', () => { expect(e

changelog

10.1.0

  • Add R.assertType and R.convertToType methods

  • Fix issue with exports in old Node.js versions - Discussion #768

  • Fix deno release as it was not possible for users to import version 10.0.0

10.0.1

  • Fix issue with R.unwind/R.pick typings - Issue #766

10.0.0

This is major revamp of Rambda library:

  • R.pipe is the recommended method for TypeScript chaining.

  • All methods should be useful to work inside R.pipe chain. If method doesn't have clear use case inside R.pipe, it is removed as part of this revamp.

  • There will be only one way to use each method. For example, R.add can be used only with R.add(1)(2), i.e. it doesn't support R.add(1, 2). This helps with testing and also with TypeScript definitions. This aligns with TypeScript focused approach of this library.

  • Confusing methods are removed. For example, R.cond and R.ifElse are removed as their usage inside R.piped makes the whole chain less readable. Such logic should be part of your codebase, not part of external library.

  • All methods that expect more than 1 input, will have to be called with R.methodName(input1)(input2) or R.methodName(input1, input2)(input3). This is to make TypeScript definitions easier to maintain.

  • Optimize many methods to better work in TypeScript context with R.pipe. The focus was passing objects through the R.pipe chain.

  • Add R.pipe supports up to 20 functions, i.e. chain can be 20 functions long.

  • R.chain is renamed to R.flatMap

  • R.comparator is renamed to R.sortingFn

  • Remove following methods:

-- Lenses - R.lens, R.lensProp, R.lensPath, R.view, R.set, R.over -- T, F -- add -- addIndex, addIndexRight -- always -- ap -- applySpec -- applyTo -- assoc, assocPath, dissoc, dissocPath -- binary -- bind -- call -- collectBy -- compose -- composeWith -- cond -- converge -- curry -- difference, differenceWith -- divide, multiply, subtract -- endsWith/startsWith -- flip -- forEachObjIndexed -- fromPairs -- gte, lte, lt, gt -- identical -- ifElse -- insert -- juxt -- length -- mapObjIndexed -- mergeAll, mergeLeft, mergeDeepLeft, mergeDeepRight -- move -- partitionIndexed -- pickAll -- pickBy -- repeat -- splitWhen -- toLower/toUpper -- unapply -- unnest -- update -- without

  • Add following methods:

-- R.pipeAsync -- R.addProp -- R.createObjectFromKeys -- R.mapAsync -- R.mapParallelAsync -- R.ascend/R.descend -- R.shuffle -- R.permutations -- R.compact -- R.rejectObject -- R.findNth -- R.combinations -- R.sortByPath -- R.sortByPathDescending -- R.sortByDescending -- R.flattenObject -- R.addPropToObjects

  • Rename following methods:

-- modifyItemAtIndex -> adjust -- checkObjectWithSpec -> where -- objectIncludes -> whereEq -- modify -> modifyProp -- chain -> flatMap -- mapObjIndexed -> mapObject

_ Regarding using object as input with TypeScript in methods such as R.map/filter - this feature is no longer supported in TypeScript as it has multiple issues when using inside pipes. In JS, it still works as before. Following methods are affected:

-- R.map -- R.mapIndexed -- R.filter -- R.reject

  • Regarding using string as path input in R.omit, R.pick and R.path with TypeScript - now it require explicit definition of expected return type.

  • Revert adding stopper logic in R.reduce - https://github.com/selfrefactor/rambda/pull/630

  • Remove use of Dictionary custom interface and use more appropriate Record<PropertyType, ...>

  • Remove use of Record<string, ...> in favour of Record<PropertyType, ...>

  • Add TypeScript definition to handle common case of R.filter(Boolean) that will turn Array<T | undefined> to Array<T>.

  • Regarding using object with R.forEach in TypeScript - this is no longer supported. Again, JS version still works with objects.

  • head/last - empty array as input will return undefined, but never

  • assocPath - stop supporting curring of type (x)(y)(z)

  • Stop support string inputs for some methods, since it was hard to correctly type them in TypeScript.

-- append/prepend

  • Change R.range to work with descending order.

  • Remove rambda/immutable as import option as it is hard to support in the new context.

  • Sync with typing of @types/ramda:

-- allPass -- anyPass -- append -- both -- countBy -- drop -- dropLast -- dropRepeatsBy -- either -- filter -- forEach -- keys -- map -- mergeAll -- modify -- modifyPath -- omit -- partition -- pluck -- prepend -- propEq -- where -- whereAny

  • Sync with typing of remeda:

-- filter -- reject -- map -- mapObject -- toPairs -- partition

-- objOf -- pluck -- mergeWith

9.4.2

  • Fix TS issue when R.take is used as part of R.pipe.

Moving away from Ramda types which are problematic in this case:

const data = ['foo', 'bar', 'baz', 'qux']
const result = piped(
    data,
    filter(
        x => x.length >= 2
    ),
    takeLast(2),
)

9.4.1

  • Fix bug with R.differenceWith when two arrays has same length - Issue #757

  • Allow path input to not be transformed when string numbers are there - Issue #750

9.4.0

  • Fix deno release

  • Fix too strict true condition in R.ifElse - Issue #750

  • Change R.groupBy typings to match @types/ramda typings

9.3.0

  • Breaking change in relation to TS typings of R.assoc, R.dissoc and R.modify - https://github.com/ramda/types/pull/37

  • Add R.isNotEmpty as it is new method in Ramda

  • Fix R.head/R.last TS definition - It returns undefined if array has length of 0. Before

9.2.1

9.2.0

9.1.1

9.1.0

Add these methods

  • insert
  • insertAll
  • lt
  • lte
  • isNotNil
  • pickBy
  • pathSatisfies
  • swap
  • mergeDeepLeft

9.0.1

  • Fix bad TS typings, due to missing declaration - Issue #716

9.0.0

Breaking change in TS definitions of lenses as now they are synced to Ramda types.

  • Add R.sortWith - Issue #707

  • Add R.innerJoin, R.gt, R.gte, R.reduceBy, R.hasIn

8.6.0

  • Wrong typing for R.dissocPath - Issue #709

  • Update build dependencies

8.5.0

  • Revert changes in R.anyPass introduced in 8.4.0 release. The reason is that the change was breaking the library older than 5.2.0 TypeScript.

  • Wrong R.partial TS definition - Issue #705

  • Add R.dropRepeatsBy

  • Add R.empty

  • Add R.eqBy

  • Add R.forEachObjIndexed

8.4.0

  • Add R.dissocPath

  • Fix TS definitions of R.head/R.last and add missing handle of empty string

  • Add R.removeIndex - method was before only in Rambdax, but now since R.dissocPath is using it, it is added to main library.

  • Allow R.omit to pass numbers as part of properties to omit, i.e. R.omit(['a', 1], {a: {1: 1, 2: 2}})

  • R.keys always returns strings - MR #700

  • Improve R.prepend/R.append type interference - MR #699

  • Change R.reduce TS definitions so index is always received - MR #696

  • Functions as a type guard in R.anyPass TS definitions - MR #695

  • Fix R.append's curried type - MR #694

  • Fix cannot compare errors in Deno with R.equals - Issue #704.

  • Fix cannot compare BigInt with R.equals

8.3.0

Add the following methods:

  • binary
  • call
  • collectBy
  • comparator
  • composeWith

8.2.0

Add the following methods:

  • addIndex
  • addIndexRight
  • ap
  • aperture
  • applyTo
  • ascend
  • descend

8.1.0

  • Fix input order of TS definitions for R.propEq method - Issue #688. The issue was due to 8.0.0 was shipped with TS definitions of 7.5.0 release.

  • Add R.differenceWith method - Issue #91

8.0.0

  • handle falsy values in merge methods - https://github.com/ramda/ramda/pull/3222

  • R.head/R.last don't return undefined for non-empty arrays

  • R.type supports dates in TS definition - Rambda already did support dates in JS.

  • Improve typings of R.endsWith/startsWith with regard to string input. - PR #622

  • Handle list as falsy value in R.reduce - Ramda MR

  • R.nop is removed - it will be moved to Rambdax as R.noop

  • R.includes is no longer using string literal in TypeScript definitions

Reason for breaking change - synchronize with Ramda 0.29.0 release:

7.5.0

  • IMPORTANT: Remove export property in package.json in order to allow Rambda support for projects with "type": "module" in package.json - Issue #667

  • Add R.unnest - Rambdax issue 89

  • R.uniq is not using R.equals as Ramda does - Issue #88

  • Fix R.path(['non','existing','path'], obj) TS definition as 7.4.0 release caused TS errors - Issue #668

7.4.0

  • Synchronize with @types/ramda - R.prop, R.path, R.pickAll

  • Remove esm Rollup output due to tree-shaking issues.

  • Upgrade all dev dependencies.

7.3.0

  • Important - changing import declaration in package.json in order to fix tree-shaking issue - Issue #647

  • Add R.modify

  • Allow multiple inputs in TypeScript versions of R.anyPass and R.allPass - Issue #642

  • Using wrong clone of object in R.mergeDeepRight - Issue #650

  • Missing early return in R.where - Issue #648

  • R.allPass doesn't accept more than 1 parameters for function predicates- Issue #604

7.2.1

7.2.0

  • Wrong R.update if index is -1 - PR #593

  • Wrong curried typings in R.anyPass - Issue #642

  • R.modifyPath not exported - Issue #640

  • Add new method R.uniqBy. Implementation is coming from Ramda MR#2641

  • Apply the following changes from @types/rambda:

-- https://github.com/DefinitelyTyped/DefinitelyTyped/commit/bab47272d52fc7bb81e85da36dbe9c905a04d067

-- Improve R.ifElse typings - https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59291

-- Make R.propEq safe for null/undefined arguments - https://github.com/ramda/ramda/pull/2594/files

7.1.4

  • R.mergeRight not found on Deno import - Issue #633

7.1.0

  • Add R.mergeRight - introduced by Ramda's latest release. While Ramda renames R.merge, Rambda will keep R.merge.

  • Rambda's pipe/compose doesn't return proper length of composed function which leads to issue with R.applySpec. It was fixed by using Ramda's pipe/compose logic - Issue #627

  • Replace Async with Promise as return type of R.type.

  • Add new types as TypeScript output for R.type - "Map", "WeakMap", "Generator", "GeneratorFunction", "BigInt", "ArrayBuffer"

  • Add R.juxt method

  • Add R.propSatisfies method

  • Add new methods after Ramda version upgrade to 0.28.0:

-- R.count -- R.modifyPath -- R.on -- R.whereAny -- R.partialObject

7.0.3

Rambda.none has wrong logic introduced in version 7.0.0 - Issue #625

7.0.2

Rambda doesn't work with pnpm due to wrong export configuration - Issue #619

7.0.1

  • Wrong ESM export configuration in package.json - Issue #614

7.0.0

  • Breaking change - sync R.compose/R.pipe with @types/ramda. That is significant change so as safeguard, it will lead a major bump. Important - this lead to raising required TypeScript version to 4.2.2. In other words, to use Rambda you'll need TypeScript version 4.2.2 or newer.

Related commit in @types/ramda - https://github.com/DefinitelyTyped/DefinitelyTyped/commit/286eff4f76d41eb8f091e7437eabd8a60d97fc1f#diff-4f74803fa83a81e47cb17a7d8a4e46a7e451f4d9e5ce2f1bd7a70a72d91f4bc1

There are several other changes in @types/ramda as stated in this comment. This leads to change of typings for the following methods in Rambda:

-- R.unless

-- R.toString

-- R.ifElse

-- R.always

-- R.complement

-- R.cond

-- R.is

-- R.sortBy

-- R.dissoc

-- R.toPairs

-- R.assoc

-- R.toLower

-- R.toUpper

  • One more reason for the breaking change is changing of export declarations in package.json based on this blog post and this merged Ramda's PR. This also led to renaming of babel.config.js to babel.config.cjs.

  • Add R.apply, R.bind and R.unapply

  • R.startsWith/R.endsWith now support lists as inputs. This way, it matches current Ramda behavior.

  • Remove unused typing for R.chain.

  • R.map/R.filter no longer accept bad inputs as iterable. This way, Rambda behaves more like Ramda, which also throws.

  • Make R.lastIndexOf follow the logic of R.indexOf.

  • Change R.type logic to Ramda logic. This way, R.type can return Error and Set as results.

  • Add missing logic in R.equals to compare sets - Issue #599

  • Improve list cloning - Issue #595

  • Handle multiple inputs with R.allPass and R.anyPass - Issue #604

  • Fix R.length wrong logic with inputs as {length: 123} - Issue #606.

  • Improve non-curry typings of R.merge by using types from mobily/ts-belt.

  • Improve performance of R.uniqWith.

  • Wrong R.update if index is -1 - PR #593

  • Make R.eqProps safe for falsy inputs - based on this opened Ramda PR.

  • Incorrect benchmarks for R.pipe/R.compose - Issue #608

  • Fix R.last/R.head typings - Issue #609

6.9.0

Fixing R.uniq was done by improving R.indexOf which has performance implication to all methods importing R.indexOf:

  • R.includes
  • R.intersection
  • R.difference
  • R.excludes
  • R.symmetricDifference
  • R.union

  • R.without no longer support the following case - without('0:1', ['0', '0:1']) // => ['0']. Now it throws as the first argument should be a list, not a string. Ramda, on the other hand, returns an empty list - https://github.com/ramda/ramda/issues/3086.

6.8.3

  • Fix TypeScript build process with rambda/immutable - Issue #572

  • Add R.objOf method

  • Add R.mapObjIndexed method

  • Publish shorter README.md version to NPM

6.8.0

  • R.has use Object.prototype.hasOwnProperty- Issue #572

  • Expose immutable.ts typings which are Rambda typings with readonly statements - Issue #565

  • Fix R.intersection wrong order compared to Ramda.

  • R.path wrong return of null instead of undefined when path value is null - PR #577

6.7.0

  • Remove ts-toolbelt types from TypeScript definitions. Most affected are the following methods, which lose one of its curried definitions:

  • R.maxBy

  • R.minBy
  • R.pathEq
  • R.viewOr
  • R.when
  • R.merge
  • R.mergeDeepRight
  • R.mergeLeft

6.6.0

  • Change R.piped typings to mimic that of R.pipe. Main difference is that R.pipe is focused on unary functions.

  • Fix wrong logic when R.without use R.includes while it should use array version of R.includes.

  • Use uglify plugin for UMD bundle.

  • Remove dist folder from .gitignore in order to fix Deno broken package. Issue #570

  • Improve R.fromPairs typings - Issue #567

6.5.3

  • Wrong logic where R.without use R.includes while it should use the array version of R.includes

This is Ramda bug, that Rambda also has before this release - https://github.com/ramda/ramda/issues/3086

6.5.2

  • Wrong R.defaultTo typings - changes introduced in v6.5.0 are missing their TS equivalent.

  • Update dependencies

6.5.1

Fix wrong versions in changelog

6.5.0

  • R.defaultTo no longer accepts infinite inputs, thus it follows Ramda implementation.

  • R.equals supports equality of functions.

  • R.pipe doesn't use R.compose.

  • Close Issue #561 - export several internal TS interfaces and types

  • Close Issue #559 - improve R.propOr typings

  • Add CHANGELOG.md file in release files list

6.4.0

  • Close Issue #560 - apply immutable lint to TypeScript definitions

  • Close Issue #553 - fix problem with curried typings of R.prop

  • Fix wrong R.last typing

  • Upgrade all rollup related dependencies

  • R.type supports Symbol just like Ramda.

  • Remove file extension in main property in package.json in order to allow experimental-modules. See also this Ramda's PR - https://github.com/ramda/ramda/pull/2678/files

  • Import R.indexBy/R.when/R.zipObj/R.propEq/R.complement changes from recent @types/ramda release.

  • R.tryCatch stop supporting asynchronous functions; the previous behaviour is exported to Rambdax as R.tryCatchAsync

6.3.1

  • Fix missing Evolved declaration in TypeScript definition

6.3.0

  • Add R.takeLastWhile

  • Add R.dropWhile

  • Add R.eqProps

  • Add R.dropLastWhile

  • Add R.dropRepeats

  • Add R.dropRepeatsWith

  • Add R.evolve

  • Add typings for R.takeWhile when iterable is a string

6.2.0

  • Add R.props

  • Add R.zipWith

  • Add R.splitAt

  • Add R.splitWhen

  • Close Issue #547 - restore readonly declaration in TypeScript definitions.

  • R.append/R.prepend now work only with arrays just like Ramda. Previous behaviour was for them to work with both arrays and strings.

  • Sync R.pluck typings with @types/ramda as there was a tiny difference.

6.1.0

  • Fix R.and wrong definition, because the function doesn't convert the result to boolean. This introduce another difference with @types/ramda.

  • Add R.once

  • Add R.or

6.0.1

  • Fix typing of R.reject as it wrongly declares that with object, it pass property to predicate.

6.0.0

  • Breaking change - R.map/R.filter/R.reject/R.forEach/R.partition doesn't pass index as second argument to the predicate, when looping over arrays. The old behaviour of map, filter and forEach can be found in Rambdax methods R.mapIndexed, R.filterIndexed and R.forEachIndexed.

  • Breaking change - R.all/R.none/R.any/R.find/R.findLast/R.findIndex/R.findLastIndex doesn't pass index as second argument to the predicate.

  • Change R.assocPath typings so the user can explicitly sets type of the new object

  • Typings of R.assoc match its @types/ramda counterpart.

  • Simplify R.forEach typings

  • Remove ReadonlyArray<T> pattern from TypeScript definitions - not enough value for the noise it adds.

5.13.1

  • Fix wrong R.takeWhile

5.13.0

  • Add R.takeWhile method

  • Fix R.lensPath issue when using string as path input. The issue was introduced when fixing Issue #524 in the previous release.

5.12.1

  • Close Issue #524 - wrong R.assocPath when path includes numbers

  • R.includes throws on wrong input, i.e. R.includes(1, null)

5.12.0

  • Add R.move method

  • Add R.union method

  • Close Issue #519 - ts-toolbelt needs other type of export with --isolatedModules flag

  • Change R.when implementation and typings to match those of Ramda

  • R.over and R.set use R.curry instead of manual currying

  • R.lensPath typings support string as path, i.e. 'a.b' instead of ['a', 'b']

  • R.equals now supports negative zero just like Ramda.equals

  • R.replace uses R.curry

5.11.0

Forgot to export R.of because of wrong marker in files/index.d.ts

5.10.0

Close Issue #514 - wrong R.length with empty string

Close Issue #511 - error in ts-toolbelt library

Close Issue #510 - R.clamp should throw if min argument is greater than max argument

  • PR #508 - add R.of

  • Definition of R.curry are not same as those of @types/ramda

  • Definitions of R.either is same as that of R.both

  • Definitions of R.ifElse no longer use any type

  • Definition of R.flatten requires passing type for the output

  • Fix definition of R.propOr, R.dissoc

  • Fix curried definitions of R.take, R.takeLast, R.drop and R.dropLast

  • 5.9.0

  • R.pickAll definition allows passing string as path to search.

  • R.propEq definition is now similar to that in @types/ramda.

  • R.none matches R.all implementation and pass index as second argument to predicate input.

  • R.reduce - drop support for object as iterable. Now it throws the same error as Ramda. Also instead of returning the initial value when iterable is undefined, now it throws.

Add index as additional argument to the TypeScript definitions of the following methods:

  • R.all
  • R.find
  • R.findLast
  • R.findIndex
  • R.findLastIndex

  • 5.8.0

Add R.mergeAll Add R.mergeDeepRight Add R.mergeLeft Add R.partition Add R.pathEq Add R.tryCatch Add R.unless Add R.whereEq Add R.where

  • Add R.last typing for empty array

  • 5.7.0 Revert PR #469 as R.curry was slow | Also now R.flip throws if arity is greater than or equal to 5

  • 5.6.3 Merge several PRs of @farwayer

  • PR #482 - improve R.forEach performance by not using R.map

  • PR #485 - improve R.map performance

  • PR #482 - improve R.reduce performance

  • Fix missing high arity typings for R.compose/pipe

  • R.merge definitions match those of @types/ramda

  • Remove dist folder from Rambda repo

  • 5.6.2

Close Issue #476 - typesafe R.propEq definitions

Approve PR #477 - fix R.groupWith when list length is 1

  • 5.6.1

Update ts-toolbelt files as now there is update pipeline for it.

Approve PR #474 - intruduce internal isArray helper

  • 5.6.0

Approve PR #469 - R.flip supports any arity | implement R.curry with R.curryN add R.applySpec

  • 5.5.0

Close Issue #464 - R.flip should handle functions with arity above 2

Close Issue #468 - fs-extra should be dev dependency as it was wrongly added as production dependency in 5.2.0

R.flip typings now match @types/ramda typings

Add R.hasPath method

Add R.mathMod typings

  • 5.4.3

Fix R.omit typings

  • 5.4.2

Fix R.pick typings

Close Issue #460 - R.paths should be curried

  • 5.4.1

Close Issue #458 - wrong R.propIs typing

  • 5.4.0

Close Issue #408 - add R.chain

  • 5.3.0

Close Issue #430 - add R.when

Also restore R.converge, R.findLast, R.findLastIndex and R.curryN as I have forgotten to export them when releasing 5.2.0.

  • 5.2.1

Fix TypeScript comment for every method

  • 5.2.0

Release new documentation site

Ramda repo now holds all Rambdax methods and tests

  • 5.1.1

Add R.converge and R.curryN from PR #412

Close Issue #410 - wrong implementation of R.groupWith

Close Issue #411 - change the order of declared R.map typings rules

  • 5.0.0

Move R.partialCurry to Rambdax(reason for major bump).

Use new type of export in TypeScript definitions.

Approve PR #381 - add R.applySpec

  • 4.6.0

Approve PR #375 - add lenses(Thank you @synthet1c)

Add R.lens

Add R.lensIndex

Add R.lensPath

Add R.lensProp

Add R.over

Add R.set

Add R.view

Sync with Ramda 0.27

Add R.paths

Add R.xor

Close Issue #373

Add R.cond

  • 4.5.0 Add R.clamp

  • 4.4.2 Improve R.propOr typings

  • 4.4.1 Make R.reject has the same typing as R.filter

  • 4.4.0 Several changes:

Close Issue #317 - add R.transpose

Close Issue #325 - R.filter should return equal values for bad inputs null and undefined

Approve suggestion for R.indexBy to accept string not only function as first argument.

Edit of R.path typings

  • 4.2.0 Approve PR #314 - add R.and

  • 4.1.1 Add missing typings for R.slice

  • 4.1.0 Add R.findLast and R.findLastIndex

  • 4.0.2 Fix R.isEmpty wrong behaviour compared to the Ramda method

  • 4.0.1 Approve PR #289 - remove console.log in R.values method

  • 4.0.0 Multiple breaking changes as Rambda methods are changed in order to increase the similarity between with Ramda

Add to Differences:

R.type can return 'NaN'

R.compose doesn't pass `this` context

R.clone doesn't work with number, booleans and strings as input

All breaking changes:

-- R.add works only with numbers

-- Fix R.adjust which had wrong order of arguments

-- R.adjust works when index is out of bounds

-- R.complement support function with multiple arguments

-- R.compose/pipe throws when called with no argument

-- R.clone works with Date value as input

-- R.drop/dropLast/take/takeLast always return new copy of the list/string

-- R.take/takeLast return original list/string with negative index

-- R.equals handles NaN and RegExp types

-- R.type/R.equals supports new Boolean/new Number/new Date/new String expressions

-- R.has works with non-object

-- R.ifElse pass all arguments

-- R.length works with bad input

-- R.propEq work with bad input for object argument

-- R.range work with bad inputs

-- R.times work with bad inputs

-- R.reverse works with strings

-- R.splitEvery throws on non-positive integer index

-- R.test throws just like Ramda when first argument is not regex

-- R.values works with bad inputs

-- R.zipObj ignores extra keys

  • 3.3.0

This is pre 4.0.0 release and it contains all of the above changes

Close issue #287 - ts-toolbelt directory was changed but not reflected in files property in package.json

  • 3.2.5

Close issue #273 - ts-toolbelt needs other type of export when isolatedModules TypeScript property

Close issue #245 - complete typings tests for methods that have more specific TypeScript definitions

  • 3.2.1 Fast fix for issue #273 - messed up typings

  • 3.2.0 There are several changes:

Close issue #263 - broken curry typing solved by ts-toolbelt local dependency.

Add R.partialCurry typings.

Approve PR #266 that adds R.slice method.

  • 3.1.0 This might be breaking change for TypeScript users, as very different definitions are introduced. With the previous state of the definitions, it was not possible to pass dtslint typings tests.

  • R.either and R.both supports multiple arguments as they should.

  • Several methods added by @squidfunk - R.assocPath, R.symmetricDifference, R.intersperse, R.intersection and R.difference

  • 3.0.1 Close issue #234 - wrong curry typing

  • 3.0.0 Deprecate R.contains, while R.includes is now following Ramda API(it uses R.equals for comparision)

  • 2.14.5 R.without needs currying

  • 2.14.4 Close issue #227 - add index as third argument of R.reduce typings

  • 2.14.2 Use R.curry with R.reduce as manual curry there didn't work as expected.

  • 2.14.1 Fix wrong typescript with R.head - PR #228 pushed by @tonivj5

  • 2.14.0 Add R.groupWith by @selfrefactor | Add R.propOr, R.mathMod, R.mean, R.median, R.negate, R.product by @ku8ar

  • 2.13.0 Add R.identical - PR #217 pushed by @ku8ar

  • 2.12.0 Add R.propIs - PR #213 and add R.sum - issue #207

  • 2.11.2 Close Rambdax issue #32 - wrong R.type when function is input

  • 2.11.1 Approve PR #182 - Changed typings to allow object as input to R.forEach and R.map

  • 2.11.0 Approve PR #179 - R.adjust handles negative index; R.all doesn't need R.filter

  • 2.10.2 Close issue #175 - missing typescript file

  • 2.10.0 Approve huge and important PR #171 submitted by @helmuthdu - Add comments to each method, improve TypeScript support

  • 2.9.0 R.toPairs and R.fromPairs

  • 2.8.0 Approve PR #165 R.clone

  • 2.7.1 expose src | Discussed at issue #147

  • 2.7.0 Approve PR #161 R.isEmpty

  • 2.6.0 R.map, R.filter and R.forEach pass original object to iterator as third argument | Discussed at issue #147

  • 2.5.0 Close issue #149 Add R.partial | R.type handles NaN

  • 2.4.0 Major bump of Rollup; Stop building for ES5

  • 2.3.1 Close issue #90 | Add string type of path in R.pathOr

  • 2.3.0 Close issue #89 | Fix missing Number TS definition in R.type

  • 2.2.0 R.defaultTo accepts indefinite number of input arguments. So the following is valid expression: const x = defaultTo('foo',null, null, 'bar')

  • 2.1.0 Restore R.zip using WatermelonDB implementation.

  • 2.0.0 Major version caused by removing of R.zip and R.addIndex. Issue #85 rightfully finds that the implementation of R.addIndex is not correct. This led to removing this method and also of R.zip as it had depended on it. The second change is that R.map, R.filter are passing array index as second argument when looping over arrays. The third change is that R.includes will return false if input is neigher string nor array. The previous behaviour was to throw an error. The last change is to increase the number of methods that are passing index as second argument to the predicate function.

  • 1.2.6 Use src folder instead of modules

  • 1.2.5 Fix omit typing
  • 1.2.4 Add missing TypeScript definitions - PR#82
  • 1.2.2 Change curry method used across most of library methods
  • 1.2.1 Add R.assoc | fix passing undefined to R.map and R.merge issue #77
  • 1.2.0 Add R.min, R.minBy, R.max, R.maxBy, R.nth and R.keys
  • 1.1.5 Close issue #74 R.zipObj
  • 1.1.4 Close issue #71 CRA fail to build rambda
  • 1.1.3 Approve PR #70 implement R.groupBy | Close issue #69
  • 1.1.2 Approve PR #67 use babel-plugin-annotate-pure-calls
  • 1.1.1 Approve PR #66 R.zip
  • 1.1.0 R.compose accepts more than one input argument issue #65
  • 1.0.13 Approve PR #64 R.indexOf
  • 1.0.12 Close issue #61 make all functions modules
  • 1.0.11 Close issue #60 problem with babelrc
  • 1.0.10 Close issue #59 add R.dissoc
  • 1.0.9 Close issue #58 - Incorrect R.equals
  • 1.0.8 R.map and R.filter pass object properties when mapping over objects
  • 1.0.7 Add R.uniqWith
  • 1.0.6 Close issue #52 - ES5 compatible code
  • 1.0.5 Close issue #51
  • 1.0.4 Close issue #50 - add R.pipe typings
  • 1.0.3 R.ifElse accept also boolean as condition argument
  • 1.0.2 Remove typedDefaultTo and typedPathOr | Add R.pickAll and R.none
  • 1.0.0 Major change as build is now ES6 not ES5 compatible (Related to issue #46)| Making Rambda fully tree-shakeable| Edit TypeScript definition
  • 0.9.8 Revert to ES5 compatible build - issue #46
  • 0.9.7 Refactor for Rollup tree-shake | Remove R.padEnd and R.padStart
  • 0.9.6 Close issue #44 - R.reverse mutates the array
  • 0.9.5 Close issue #45 - invalid TypeScript typings
  • 0.9.4 Add R.reject and R.without (PR#41 PR#42) | Remove 'browser' field in package.json due to Webpack bug 4674
  • 0.9.3 Add R.forEach and R.times
  • 0.9.2 Add TypeScript definitions
  • 0.9.1 Close issue #36 - move current behaviour of defaultTo to a new method typedDefaultTo; make defaultTo follow Ramda spec; add pathOr; add typedPathOr.
  • 0.9.0 Add R.pipe PR#35
  • 0.8.9 Add R.isNil
  • 0.8.8 Migrate to ES modules PR33 | Add R.flip to the API | R.map/filter works with objects
  • 0.8.7 Change Webpack with Rollup - PR29
  • 0.8.6 Add R.tap and R.identity
  • 0.8.5 Add R.all, R.allPass, R.both, R.either and R.complement
  • 0.8.4 Learning to run yarn test before yarn publish the hard way
  • 0.8.3 Add R.always, R.T and R.F
  • 0.8.2 Add concat, padStart, padEnd, lastIndexOf, toString, reverse, endsWith and startsWith methods
  • 0.8.1 Add R.ifElse
  • 0.8.0 Add R.not, R.includes | Take string as condition for R.pick and R.omit
  • 0.7.6 Fix incorrect implementation of R.values
  • 0.7.5 Fix incorrect implementation of R.omit
  • 0.7.4 issue #13 - Fix R.curry, which used to return incorrectly function when called with more arguments
  • 0.7.3 Close issue #9 - Compile to es2015; Approve PR #10 - add R.addIndex to the API
  • 0.7.2 Add Promise support for R.type
  • 0.7.1 Close issue #7 - add R.reduce to the API
  • 0.7.0 Close issue #5 - change name of curry to partialCurry; add new method curry, which works just like Ramda's curry
  • 0.6.2 Add separate documentation site via docsify