npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

rambda

v10.3.4

Published

Lightweight and faster alternative to Ramda with included TS definitions

Downloads

11,501,049

Readme

Rambda

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

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

❯ Example use

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

:exclamation: IMPORTANT - all methods are tested to deliver correct types when they are part of R.pipe/R.pipeAsync chains.

In other words:

R.filter(x => x > 1)([1,2,3])

might trigger TS error as it not the same as


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.

From version 10.0.0 onwards, Rambda is no longer aiming to be drop-in replacement for Ramda.

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

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

addProp<T extends object, P extends PropertyKey, V extends unknown>(
	prop: P,
	value: V
): (obj: T) => MergeTypes<T & Record<P, V>>;
export function addProp(key, value) {
  return obj => ({ ...obj, [key]: value })
}
import { addProp } from "./addProp.js"

test('happy', () => {
	const result = addProp('a', 1)({ b: 2 })
	const expected = { a: 1, b: 2 }

	expect(result).toEqual(expected)
})
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

addPropToObjects<
  T extends object,
  K extends string,
  R
>(
	property: K,
  fn: (input: T) => R
): (list: T[]) => MergeTypes<T & { [P in K]: R }>[];
import { mapFn } from './map.js'

export function addPropToObjects (
	property, 
	fn
){
	return listOfObjects => mapFn(
		(obj) => ({
			...(obj),
			[property]: fn(obj)
		}), 
		listOfObjects
	)
}
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' },
		])
})
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

all<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
export function all(predicate) {
  return list => {
    for (let i = 0; i < list.length; i++) {
      if (!predicate(list[i])) {
        return false
      }
    }

    return true
  }
}
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()
})
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

allPass<F extends (...args: any[]) => boolean>(predicates: readonly F[]): F;
export function allPass(predicates) {
  return input => {
    let counter = 0
    while (counter < predicates.length) {
      if (!predicates[counter](input)) {
        return false
      }
      counter++
    }

    return true
  }
}
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([])
})
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(predicate)(list)
// => true

Try this R.any example in Rambda REPL

any<T>(predicate: (x: T) => boolean): (list: T[]) => boolean;
export function any(predicate) {
  return list => {
    let counter = 0
    while (counter < list.length) {
      if (predicate(list[counter], counter)) {
        return true
      }
      counter++
    }

    return false
  }
}
import { any } from './any.js'

const list = [1, 2, 3]

test('happy', () => {
  expect(any(x => x > 2)(list)).toBeTruthy()
})
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

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;
...
...
export function anyPass(predicates) {
  return input => {
    let counter = 0
    while (counter < predicates.length) {
      if (predicates[counter](input)) {
        return true
      }
      counter++
    }

    return false
  }
}
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()
})
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 result = R.append('foo')(['bar', 'baz'])
// => ['bar', 'baz', 'foo']

Try this R.append example in Rambda REPL

append<T>(el: T): (list: T[]) => T[];
append<T>(el: T): (list: readonly T[]) => T[];
import { cloneList } from './_internals/cloneList.js'

export function append(x) {
  return list => {
    const clone = cloneList(list)
    clone.push(x)

    return clone
  }
}
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'])
})
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

ascend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
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)
}
}
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}])
})
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.

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

checkObjectWithSpec<T>(spec: T): <U>(testObj: U) => boolean;
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
  }
}
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)
})
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

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

complement<T extends any[]>(predicate: (...args: T) => unknown): (...args: T) => boolean;
export function complement(fn) {
  return (...input) => !fn(...input)
}
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()
})
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

concat<T>(x: T[]): (y: T[]) => T[];
concat(x: string): (y: string) => string;
export function concat(x) {
  return y => (typeof x === 'string' ? `${x}${y}` : [...x, ...y])
}
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.

convertToType<T>(x: unknown) : T;
export function convertToType(x) {
  return x
}
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

count<T>(predicate: (x: T) => boolean): (list: T[]) => number;
import { isArray } from './_internals/isArray.js'

export function count(predicate) {
  return list => {
    if (!isArray(list)) {
      return 0
    }

    return list.filter(x => predicate(x)).length
  }
}
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)
})
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

countBy<T>(fn: (x: T) => string | number): (list: T[]) => { [index: string]: number };
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
  }
}
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,
  })
})
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

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 };
export function createObjectFromKeys(fn) {
	return keys => {
		const result = {}
		keys.forEach((key, index) => {
			result[key] = fn(key, index)
		})

		return result
	}
}
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)
})

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

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

defaultTo<T>(defaultValue: T): (input: unknown) => T;
function isFalsy(input) {
  return input === undefined || input === null || Number.isNaN(input) === true
}

export function defaultTo(defaultArgument) {
  return input => isFalsy(input) ? defaultArgument : input
}
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')
})
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

descend<T>(fn: (obj: T) => Ord): (a: T, b: T)=> Ordering;
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

drop<T>(howMany: number): (list: T[]) => T[];
export function drop(howManyToDrop, ) {
  return list => list.slice(howManyToDrop > 0 ? howManyToDrop : 0)
}
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])
})
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.

dropLast<T>(howMany: number): (list: T[]) => T[];
export function dropLast(numberItems) {
  return list => (numberItems > 0 ? list.slice(0, -numberItems) : list.slice())
}
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

dropLastWhile<T>(predicate: (x: T, index: number) => boolean): (list: T[]) => T[];
dropLastWhile<T>(predicate: (x: T) => boolean): (list: T[]) => T[];
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()
  }
}
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

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

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

dropWhile<T>(predicate: (x: T, index: number) => boolean): (list: T[]) => T[];
dropWhile<T>(predicate: (x: T) => boolean): (list: T[]) => T[];
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
  }
}
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)
})
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[]
  })
})

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

duplicateBy


duplicateBy<T, U>(fn: (x: T) => U): (list: T[]) => T[]
const list = [{a:1}, {a:2}, {a:1}]
const result = R.duplicateBy(x => x, list)

// => [{a:1}]

Try this R.duplicateBy example in Rambda REPL

duplicateBy<T, U>(fn: (x: T) => U): (list: T[]) => T[];
import { _Set } from '../src/_internals/set.js'

export function duplicateBy(fn) {
  return list => {
    const set = new _Set()

    return list.filter(item => !set.checkUniqueness(fn(item)))
  }
}
import { duplicateBy } from './duplicateBy.js'

test('happy', () => {
  expect(duplicateBy(Math.abs)([-2, -1, 0, 1, 2])).toEqual([1,2])
})

test('returns an empty array for an empty array', () => {
  expect(duplicateBy(Math.abs)([])).toEqual([])
})

test('uses R.uniq', () => {
  const list = [{ a: 1 }, { a: 2 }, { a: 1 }]
  const expected = [{ a: 1 }]
  expect(duplicateBy(x => x)(list)).toEqual(expected)
})

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

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

eqBy<T>(fn: (x: T) => unknown, a: T): (b: T) => boolean;
import { equalsFn } from './equals.js'

export function eqBy(fn, a) {
  return b => equalsFn(fn(a), fn(b))
}
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

eqProps<T, K extends keyof T>(prop: K, obj1: T): (obj2: T) => boolean;
import { equalsFn } from './equals.js'

export function eqProps(property, objA) {
  return objB => equalsFn( objA[property], objB[property] )
}
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()
})
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

equals<T>(x: T, y: T): boolean;
equals<T>(x: T): (y: T) => boolean;
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)
}
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 obj