/rambda

Faster and smaller alternative to Ramda

Primary LanguageJavaScriptMIT LicenseMIT

CircleCI codecov dependencies Status

Rambda

Faster alternative to Ramda - Documentation

Example use

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

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

You can test this example in Rambda's REPL

Rambda's advantages

  • Tree-shaking

Currently Rambda is more tree-shakable than Ramda


  • Speed

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

Click to expand all benchmark results

Note that some methods benchmarked only with Ramda and Rambda(i.e. no Lodash), are called with and without curring. This is done in order to give more detailed performance feedback.

method Rambda Ramda Lodash
add 🚀 Fastest 28.16% slower 76.17% slower
adjust 🚀 Fastest 2.8% slower 🔳
all 🚀 Fastest 89.64% slower 🔳
allPass 🚀 Fastest 98.48% slower 🔳
any 🚀 Fastest 92.1% slower 29.4% slower
anyPass 🚀 Fastest 98.67% slower 🔳
append 🚀 Fastest 85.14% slower 🔳
assoc 76.71% slower 63.5% slower 🚀 Fastest
clone 🚀 Fastest 93.55% slower 88.95% slower
compose 🚀 Fastest 95.09% slower 79.91% slower
curry 🚀 Fastest 42.95% slower 🔳
defaultTo 🚀 Fastest 41.61% slower 🔳
drop 🚀 Fastest 89.2% slower 🔳
dropLast 🚀 Fastest 91.53% slower 🔳
equals 🚀 Fastest 84.87% slower 59.82% slower
filter 🚀 Fastest 72.63% slower 11.78% slower
find 🚀 Fastest 47.89% slower 60.19% slower
findIndex 🚀 Fastest 90.6% slower 84.75% slower
flatten 10.31% slower 96.42% slower 🚀 Fastest
indexOf 🚀 Fastest 69.38% slower 0.64% slower
init 🚀 Fastest 94.17% slower 2.63% slower
isEmpty 37.68% slower 92.85% slower 🚀 Fastest
last 🚀 Fastest 99.02% slower 3.5% slower
map 🚀 Fastest 87.72% slower 23.59% slower
match 🚀 Fastest 52.01% slower 🔳
merge 🚀 Fastest 29.34% slower 67.66% slower
omit 🚀 Fastest 72.93% slower 97.97% slower
path 0.34% slower 52.76% slower 🚀 Fastest
pick 🚀 Fastest 24.06% slower 88.13% slower
prop 🚀 Fastest 94.38% slower 🔳
propEq 🚀 Fastest 90.34% slower 🔳
range 🚀 Fastest 63.45% slower 50.56% slower
reduce 71.84% slower 84.24% slower 🚀 Fastest
repeat 55.51% slower 83.45% slower 🚀 Fastest
replace 🚀 Fastest 35.85% slower 4.98% slower
sort 🚀 Fastest 28.43% slower 🔳
sortBy 🚀 Fastest 16.52% slower 72.48% slower
split 🚀 Fastest 56.27% slower 28.78% slower
splitEvery 🚀 Fastest 74.75% slower 🔳
take 🚀 Fastest 96% slower 26.07% slower
takeLast 🚀 Fastest 96.37% slower 28.53% slower
test 🚀 Fastest 86.86% slower 🔳
type 19.76% slower 🚀 Fastest 🔳
uniq 99.56% slower 96.54% slower 🚀 Fastest
update 🚀 Fastest 87.94% slower 🔳

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

  • comma notation for R.pick and R.omit

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

R.pick('a,b', {a: 1 , b: 2, c: 3} })

// No space allowed between properties

  • Typescript included

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

  • More generic methods

Ramda has an overwhelming list of methods, as one could get lost putting all the methods in one's head. Rambda's much smaller number of total methods(109) I see as advantage compared to the 255 of Ramda.

Ramda methods has plenty of really deep FP Methods, which are in fact quite useful, but they come at the price of added complexity. Such complex logics are in practice rarely needed.

You can check the list with missing Ramda methods in Rambda list to assure that Rambda doesn't have any important misses.

Install

  • Use yarn add rambda for Webpack and Node.js usage

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

https://unpkg.com/rambda@4.0.1/dist/rambda.umd.js

Differences between Rambda and Ramda

  • Rambda's type detect async functions and unresolved Promises. The returned values are 'Async' and 'Promise'.

  • Rambda's type handle Not a number values and it returns NaN in this case.

  • Rambda's path accepts dot notation('x.y' same as ['x','y'])

  • Rambda's pick and omit accept comma notation('x,y' same as ['x','y'])

  • Rambda's map, filter, reject and forEach can iterate over objects not only arrays.

  • Rambda's map and filter pass array index as second argument when mapping over arrays.

  • Rambda's defaultTo accept indefinite number of arguments when non curried, i.e. R.defaultTo(2, foo, bar, baz).

  • Rambda's adjust, all, allPass, any, anyPass, findIndex , findLastIndex and reject are passing index as second argument to the predicate function.

  • Rambda's startsWith/endsWith work only with strings, instead with array and strings.

  • Rambda's equals doesn't protect against circular structures as Ramda.equals does.

  • Rambda's flip works only for functions expecting two arguments.

  • Rambda's partial doesn't need the input arguments to be wrapped as array.

  • Rambda's partialCurry is not part of Ramda API.

  • Ramda's includes will throw an error if input is neither string nor array, while Rambda version will return false.

If you need more Ramda methods in Rambda, you may either submit a PR or check the extended version of Rambda - Rambdax. In case of the former, you may want to consult with Rambda contribution guidelines.


Expand to see all `Ramda` tests failing for `Rambda`, if you want to know in detail the difference between the two libraries

adjust

Reason for failing: ramda accepts an array-like object

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('adjust', function() {
  it('accepts an array-like object', function() {
    function args() {
      return arguments;
    }
    eq(R.adjust(2, R.add(1), args(0, 1, 2, 3)), [0, 1, 3, 3]);
  });
});

allPass

Reason for failing: ramda returns a curried function whose arity matches that of the highest-arity predicate

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('allPass', function() {
  var odd = function(n) { return n % 2 !== 0; };
  var lt20 = function(n) { return n < 20; };
  var gt5 = function(n) { return n > 5; };
  var plusEq = function(w, x, y, z) { return w + x === y + z; };
  it('returns a curried function whose arity matches that of the highest-arity predicate', function() {
    eq(R.allPass([odd, gt5, plusEq]).length, 4);
    eq(R.allPass([odd, gt5, plusEq])(9, 9, 9, 9), true);
    eq(R.allPass([odd, gt5, plusEq])(9)(9)(9)(9), true);
  });
});

anyPass

Reason for failing: ramda returns a curried function whose arity matches that of the highest-arity predicate

const eq = require('./shared/eq')
const R = require('../../../../../rambda/dist/rambda.js')

describe('anyPass', () => {
  const odd = function(n){ return n % 2 !== 0 }
  const gt20 = function(n){ return n > 20 }
  const lt5 = function(n){ return n < 5 }
  const plusEq = function(w, x, y, z){ return w + x === y + z }
  it('returns a curried function whose arity matches that of the highest-arity predicate', () => {
    eq(R.anyPass([ odd, lt5, plusEq ]).length, 4)
    eq(R.anyPass([ odd, lt5, plusEq ])(6, 7, 8, 9), false)
    eq(R.anyPass([ odd, lt5, plusEq ])(6)(7)(8)(9), false)
  })
})

both

Reason for failing: ramda supports fantasy-land

var S = require('sanctuary');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('both', function() {
  it('accepts fantasy-land applicative functors', function() {
    var Just = S.Just;
    var Nothing = S.Nothing;
    eq(R.both(Just(true), Just(true)), Just(true));
    eq(R.both(Just(true), Just(false)), Just(false));
    eq(R.both(Just(true), Nothing()), Nothing());
    eq(R.both(Nothing(), Just(false)), Nothing());
    eq(R.both(Nothing(), Nothing()), Nothing());
  });
});

clone

Reason for failing: rambda method work only with objects and arrays

var assert = require('assert');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('deep clone integers, strings and booleans', function() {
  it('clones integers', function() {
    eq(R.clone(-4), -4);
    eq(R.clone(9007199254740991), 9007199254740991);
  });
  it('clones floats', function() {
    eq(R.clone(-4.5), -4.5);
    eq(R.clone(0.0), 0.0);
  });
  it('clones strings', function() {
    eq(R.clone('ramda'), 'ramda');
  });
  it('clones booleans', function() {
    eq(R.clone(true), true);
  });
});
describe('deep clone objects', function() {
  it('clones objects with circular references', function() {
    var x = {c: null};
    var y = {a: x};
    var z = {b: y};
    x.c = z;
    var clone = R.clone(x);
    assert.notStrictEqual(x, clone);
    assert.notStrictEqual(x.c, clone.c);
    assert.notStrictEqual(x.c.b, clone.c.b);
    assert.notStrictEqual(x.c.b.a, clone.c.b.a);
    assert.notStrictEqual(x.c.b.a.c, clone.c.b.a.c);
    eq(R.keys(clone), R.keys(x));
    eq(R.keys(clone.c), R.keys(x.c));
    eq(R.keys(clone.c.b), R.keys(x.c.b));
    eq(R.keys(clone.c.b.a), R.keys(x.c.b.a));
    eq(R.keys(clone.c.b.a.c), R.keys(x.c.b.a.c));
    x.c.b = 1;
    assert.notDeepEqual(clone.c.b, x.c.b);
  });
});
describe('deep clone arrays', function() {
});
describe('deep clone functions', function() {
});
describe('built-in types', function() {
  it('clones RegExp object', function() {
    R.forEach(function(pattern) {
      var clone = R.clone(pattern);
      assert.notStrictEqual(clone, pattern);
      eq(clone.constructor, RegExp);
      eq(clone.source, pattern.source);
      eq(clone.global, pattern.global);
      eq(clone.ignoreCase, pattern.ignoreCase);
      eq(clone.multiline, pattern.multiline);
    }, [/x/, /x/g, /x/i, /x/m, /x/gi, /x/gm, /x/im, /x/gim]);
  });
});
describe('deep clone deep nested mixed objects', function() {
  it('clones array with mutual ref object', function() {
    var obj = {a: 1};
    var list = [{b: obj}, {b: obj}];
    var clone = R.clone(list);
    assert.strictEqual(list[0].b, list[1].b);
    assert.strictEqual(clone[0].b, clone[1].b);
    assert.notStrictEqual(clone[0].b, list[0].b);
    assert.notStrictEqual(clone[1].b, list[1].b);
    eq(clone[0].b, {a:1});
    eq(clone[1].b, {a:1});
    obj.a = 2;
    eq(clone[0].b, {a:1});
    eq(clone[1].b, {a:1});
  });
});
describe('deep clone edge cases', function() {
  it('nulls, undefineds and empty objects and arrays', function() {
    eq(R.clone(null), null);
    eq(R.clone(undefined), undefined);
    assert.notStrictEqual(R.clone(undefined), null);
    var obj = {};
    assert.notStrictEqual(R.clone(obj), obj);
    var list = [];
    assert.notStrictEqual(R.clone(list), list);
  });
});
describe('Let `R.clone` use an arbitrary user defined `clone` method', function() {
  it('dispatches to `clone` method if present', function() {
    function ArbitraryClone(x) { this.value = x; }
    ArbitraryClone.prototype.clone = function() { return new ArbitraryClone(this.value); };
    var obj = new ArbitraryClone(42);
    var arbitraryClonedObj = R.clone(obj);
    eq(arbitraryClonedObj, new ArbitraryClone(42));
    eq(arbitraryClonedObj instanceof ArbitraryClone, true);
  });
});

complement

Reason for failing: ramda supports fantasy-land

var S = require('sanctuary');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('complement', function() {
  it('accepts fantasy-land functors', function() {
    var Just = S.Just;
    var Nothing = S.Nothing;
    eq(R.complement(Just(true)), Just(false));
    eq(R.complement(Just(false)), Just(true));
    eq(R.complement(Nothing()), Nothing());
  });
});

compose

Reason for failing: ramda passes context to functions | rambda composed functions have no length

var assert = require('assert');
var jsv = require('jsverify');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('compose', function() {
  it('performs right-to-left function composition', function() {
    //  f :: (String, Number?) -> ([Number] -> [Number])
    var f = R.compose(R.map, R.multiply, parseInt);
    eq(f.length, 2);
    eq(f('10')([1, 2, 3]), [10, 20, 30]);
    eq(f('10', 2)([1, 2, 3]), [2, 4, 6]);
  });
  it('passes context to functions', function() {
    function x(val) {
      return this.x * val;
    }
    function y(val) {
      return this.y * val;
    }
    function z(val) {
      return this.z * val;
    }
    var context = {
      a: R.compose(x, y, z),
      x: 4,
      y: 2,
      z: 1
    };
    eq(context.a(5), 40);
  });
  it('can be applied to one argument', function() {
    var f = function(a, b, c) { return [a, b, c]; };
    var g = R.compose(f);
    eq(g.length, 3);
    eq(g(1, 2, 3), [1, 2, 3]);
  });
});
describe('compose properties', function() {
  jsv.property('composes two functions', jsv.fn(), jsv.fn(), jsv.nat, function(f, g, x) {
    return R.equals(R.compose(f, g)(x), f(g(x)));
});

concat

Reason for failing: ramda pass to concat method if present

var assert = require('assert');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('concat', function() {
  var z1 = {
    x: 'z1',
    concat: function(that) { return this.x + ' ' + that.x; }
  };
  var z2 = {
    x: 'z2'
  };
  it('delegates to non-String object with a concat method, as second param', function() {
    eq(R.concat(z1, z2), 'z1 z2');
  });
});

curry

Reason for failing: ramda passes context to functions

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
var jsv = require('jsverify');
var funcN = require('./shared/funcN');

describe('curry', function() {
  it('properly reports the length of the curried function', function() {
    var f = R.curry(function(a, b, c, d) {return (a + b * c) / d;});
    eq(f.length, 4);
    var g = f(12);
    eq(g.length, 3);
    var h = g(3);
    eq(h.length, 2);
    eq(g(3, 6).length, 1);
  });
  it('preserves context', function() {
    var ctx = {x: 10};
    var f = function(a, b) { return a + b * this.x; };
    var g = R.curry(f);
    eq(g.call(ctx, 2, 4), 42);
    eq(g.call(ctx, 2).call(ctx, 4), 42);
  });
  it('supports R.__ placeholder', function() {
    var f = function(a, b, c) { return [a, b, c]; };
    var g = R.curry(f);
    var _ = R.__;
    eq(g(1)(2)(3), [1, 2, 3]);
    eq(g(1)(2, 3), [1, 2, 3]);
    eq(g(1, 2)(3), [1, 2, 3]);
    eq(g(1, 2, 3), [1, 2, 3]);
    eq(g(_, 2, 3)(1), [1, 2, 3]);
    eq(g(1, _, 3)(2), [1, 2, 3]);
    eq(g(1, 2, _)(3), [1, 2, 3]);
    eq(g(1, _, _)(2)(3), [1, 2, 3]);
    eq(g(_, 2, _)(1)(3), [1, 2, 3]);
    eq(g(_, _, 3)(1)(2), [1, 2, 3]);
    eq(g(1, _, _)(2, 3), [1, 2, 3]);
    eq(g(_, 2, _)(1, 3), [1, 2, 3]);
    eq(g(_, _, 3)(1, 2), [1, 2, 3]);
    eq(g(1, _, _)(_, 3)(2), [1, 2, 3]);
    eq(g(_, 2, _)(_, 3)(1), [1, 2, 3]);
    eq(g(_, _, 3)(_, 2)(1), [1, 2, 3]);
    eq(g(_, _, _)(_, _)(_)(1, 2, 3), [1, 2, 3]);
    eq(g(_, _, _)(1, _, _)(_, _)(2, _)(_)(3), [1, 2, 3]);
  });
  it('supports @@functional/placeholder', function() {
    var f = function(a, b, c) { return [a, b, c]; };
    var g = R.curry(f);
    var _ = {'@@functional/placeholder': true, x: Math.random()};
    eq(g(1)(2)(3), [1, 2, 3]);
    eq(g(1)(2, 3), [1, 2, 3]);
    eq(g(1, 2)(3), [1, 2, 3]);
    eq(g(1, 2, 3), [1, 2, 3]);
    eq(g(_, 2, 3)(1), [1, 2, 3]);
    eq(g(1, _, 3)(2), [1, 2, 3]);
    eq(g(1, 2, _)(3), [1, 2, 3]);
    eq(g(1, _, _)(2)(3), [1, 2, 3]);
    eq(g(_, 2, _)(1)(3), [1, 2, 3]);
    eq(g(_, _, 3)(1)(2), [1, 2, 3]);
    eq(g(1, _, _)(2, 3), [1, 2, 3]);
    eq(g(_, 2, _)(1, 3), [1, 2, 3]);
    eq(g(_, _, 3)(1, 2), [1, 2, 3]);
    eq(g(1, _, _)(_, 3)(2), [1, 2, 3]);
    eq(g(_, 2, _)(_, 3)(1), [1, 2, 3]);
    eq(g(_, _, 3)(_, 2)(1), [1, 2, 3]);
    eq(g(_, _, _)(_, _)(_)(1, 2, 3), [1, 2, 3]);
    eq(g(_, _, _)(1, _, _)(_, _)(2, _)(_)(3), [1, 2, 3]);
  });
});
describe('curry properties', function() {
  jsv.property('curries multiple values', funcN(4), jsv.json, jsv.json, jsv.json, jsv.json, function(f, a, b, c, d) {
    var g = R.curry(f);
    return R.all(R.equals(f(a, b, c, d)), [
      g(a, b, c, d),
      g(a)(b)(c)(d),
      g(a)(b, c, d),
      g(a, b)(c, d),
      g(a, b, c)(d)
    ]);
  jsv.property('curries with placeholder', funcN(3), jsv.json, jsv.json, jsv.json, function(f, a, b, c) {
    var _ = {'@@functional/placeholder': true, x: Math.random()};
    var g = R.curry(f);
    return R.all(R.equals(f(a, b, c)), [
      g(_, _, c)(a, b),
      g(a, _, c)(b),
      g(_, b, c)(a),
      g(a, _, _)(_, c)(b),
      g(a, b, _)(c)
    ]);
});

difference

Reason for failing: ramda supports negative zero

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('difference', function() {
  var M = [1, 2, 3, 4];
  var M2 = [1, 2, 3, 4, 1, 2, 3, 4];
  var N = [3, 4, 5, 6];
  var N2 = [3, 3, 4, 4, 5, 5, 6, 6];
  var Z = [3, 4, 5, 6, 10];
  var Z2 = [1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8];
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.difference([0], [-0]).length, 1);
    eq(R.difference([-0], [0]).length, 1);
    eq(R.difference([NaN], [NaN]).length, 0);
    eq(R.difference([new Just([42])], [new Just([42])]).length, 0);
  });
});

dropLast

Reason for failing: ramda method can act as a transducer

var assert = require('assert');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('dropLast', function() {
  it('can act as a transducer', function() {
    var dropLast2 = R.dropLast(2);
    assert.deepEqual(R.into([], dropLast2, [1, 3, 5, 7, 9, 1, 2]), [1, 3, 5, 7, 9]);
    assert.deepEqual(R.into([], dropLast2, [1]), []);
  });
});

either

Reason for failing: ramda supports fantasy-land

var S = require('sanctuary');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('either', function() {
  it('accepts fantasy-land applicative functors', function() {
    var Just = S.Just;
    var Nothing = S.Nothing;
    eq(R.either(Just(true), Just(true)), Just(true));
    eq(R.either(Just(true), Just(false)), Just(true));
    eq(R.either(Just(false), Just(false)), Just(false));
    eq(R.either(Just(true), Nothing()), Nothing());
    eq(R.either(Nothing(), Just(false)), Nothing());
    eq(R.either(Nothing(), Nothing()), Nothing());
  });
});

endsWith

Reason for failing: rambda doesn't support arrays

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('startsWith', function() {
  it('should return true when an array ends with the provided value', function() {
    eq(R.endsWith(['c'], ['a', 'b', 'c']), true);
  });
  it('should return true when an array ends with the provided values', function() {
    eq(R.endsWith(['b', 'c'], ['a', 'b', 'c']), true);
  });
  it('should return false when an array does not end with the provided value', function() {
    eq(R.endsWith(['b'], ['a', 'b', 'c']), false);
  });
  it('should return false when an array does not end with the provided values', function() {
    eq(R.endsWith(['a', 'b'], ['a', 'b', 'c']), false);
  });
});

equals

Reason for failing: rambda doesn't support recursive data structures, objects with same enumerable properties, map/weakmap type of variables | ramda dispatches to equals method recursively

/* global Map, Set, WeakMap, WeakSet */

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('equals', function() {
  var a = [];
  var b = a;
  it('never considers Boolean primitive equal to Boolean object', function() {
    eq(R.equals(true, new Boolean(true)), false);
    eq(R.equals(new Boolean(true), true), false);
    eq(R.equals(false, new Boolean(false)), false);
    eq(R.equals(new Boolean(false), false), false);
  });
  it('never considers number primitive equal to Number object', function() {
    eq(R.equals(0, new Number(0)), false);
    eq(R.equals(new Number(0), 0), false);
  });
  it('never considers string primitive equal to String object', function() {
    eq(R.equals('', new String('')), false);
    eq(R.equals(new String(''), ''), false);
    eq(R.equals('x', new String('x')), false);
    eq(R.equals(new String('x'), 'x'), false);
  });
  var supportsSticky = false;
  try { RegExp('', 'y'); supportsSticky = true; } catch (e) {}
  var supportsUnicode = false;
  try { RegExp('', 'u'); supportsUnicode = true; } catch (e) {}
  var listA = [1, 2, 3];
  var listB = [1, 3, 2];
  var c = {}; c.v = c;
  var d = {}; d.v = d;
  var e = []; e.push(e);
  var f = []; f.push(f);
  var nestA = {a:[1, 2, {c:1}], b:1};
  var nestB = {a:[1, 2, {c:1}], b:1};
  var nestC = {a:[1, 2, {c:2}], b:1};
  it('handles recursive data structures', function() {
    eq(R.equals(c, d), true);
    eq(R.equals(e, f), true);
    eq(R.equals(nestA, nestB), true);
    eq(R.equals(nestA, nestC), false);
  });
  it('requires that both objects have the same enumerable properties with the same values', function() {
    var a1 = [];
    var a2 = [];
    a2.x = 0;
    var b1 = new Boolean(false);
    var b2 = new Boolean(false);
    b2.x = 0;
    var d1 = new Date(0);
    var d2 = new Date(0);
    d2.x = 0;
    var n1 = new Number(0);
    var n2 = new Number(0);
    n2.x = 0;
    var r1 = /(?:)/;
    var r2 = /(?:)/;
    r2.x = 0;
    var s1 = new String('');
    var s2 = new String('');
    s2.x = 0;
    eq(R.equals(a1, a2), false);
    eq(R.equals(b1, b2), false);
    eq(R.equals(d1, d2), false);
    eq(R.equals(n1, n2), false);
    eq(R.equals(r1, r2), false);
    eq(R.equals(s1, s2), false);
  });
  if (typeof ArrayBuffer !== 'undefined' && typeof Int8Array !== 'undefined') {
    var typArr1 = new ArrayBuffer(10);
    typArr1[0] = 1;
    var typArr2 = new ArrayBuffer(10);
    typArr2[0] = 1;
    var typArr3 = new ArrayBuffer(10);
    var intTypArr = new Int8Array(typArr1);
    typArr3[0] = 0;
    it('handles typed arrays', function() {
      eq(R.equals(typArr1, typArr2), true);
      eq(R.equals(typArr1, typArr3), false);
      eq(R.equals(typArr1, intTypArr), false);
    });
  }
  if (typeof Promise !== 'undefined') {
    it('compares Promise objects by identity', function() {
      var p = Promise.resolve(42);
      var q = Promise.resolve(42);
      eq(R.equals(p, p), true);
      eq(R.equals(p, q), false);
    });
  }
  if (typeof Map !== 'undefined') {
    it('compares Map objects by value', function() {
      eq(R.equals(new Map([]), new Map([])), true);
      eq(R.equals(new Map([]), new Map([[1, 'a']])), false);
      eq(R.equals(new Map([[1, 'a']]), new Map([])), false);
      eq(R.equals(new Map([[1, 'a']]), new Map([[1, 'a']])), true);
      eq(R.equals(new Map([[1, 'a'], [2, 'b']]), new Map([[2, 'b'], [1, 'a']])), true);
      eq(R.equals(new Map([[1, 'a']]), new Map([[2, 'a']])), false);
      eq(R.equals(new Map([[1, 'a']]), new Map([[1, 'b']])), false);
      eq(R.equals(new Map([[1, 'a'], [2, new Map([[3, 'c']])]]), new Map([[1, 'a'], [2, new Map([[3, 'c']])]])), true);
      eq(R.equals(new Map([[1, 'a'], [2, new Map([[3, 'c']])]]), new Map([[1, 'a'], [2, new Map([[3, 'd']])]])), false);
      eq(R.equals(new Map([[[1, 2, 3], [4, 5, 6]]]), new Map([[[1, 2, 3], [4, 5, 6]]])), true);
      eq(R.equals(new Map([[[1, 2, 3], [4, 5, 6]]]), new Map([[[1, 2, 3], [7, 8, 9]]])), false);
    });
    it('dispatches to `equals` method recursively in Set', function() {
      var a = new Map();
      var b = new Map();
      a.set(a, a);
      eq(R.equals(a, b), false);
      a.set(b, b);
      b.set(b, b);
      b.set(a, a);
      eq(R.equals(a, b), true);
    });
  }
  if (typeof Set !== 'undefined') {
    it('compares Set objects by value', function() {
      eq(R.equals(new Set([]), new Set([])), true);
      eq(R.equals(new Set([]), new Set([1])), false);
      eq(R.equals(new Set([1]), new Set([])), false);
      eq(R.equals(new Set([1, 2]), new Set([2, 1])), true);
      eq(R.equals(new Set([1, new Set([2, new Set([3])])]), new Set([1, new Set([2, new Set([3])])])), true);
      eq(R.equals(new Set([1, new Set([2, new Set([3])])]), new Set([1, new Set([2, new Set([4])])])), false);
      eq(R.equals(new Set([[1, 2, 3], [4, 5, 6]]), new Set([[1, 2, 3], [4, 5, 6]])), true);
      eq(R.equals(new Set([[1, 2, 3], [4, 5, 6]]), new Set([[1, 2, 3], [7, 8, 9]])), false);
    });
    it('dispatches to `equals` method recursively in Set', function() {
      var a = new Set();
      var b = new Set();
      a.add(a);
      eq(R.equals(a, b), false);
      a.add(b);
      b.add(b);
      b.add(a);
      eq(R.equals(a, b), true);
    });
  }
  if (typeof WeakMap !== 'undefined') {
    it('compares WeakMap objects by identity', function() {
      var m = new WeakMap([]);
      eq(R.equals(m, m), true);
      eq(R.equals(m, new WeakMap([])), false);
    });
  }
  if (typeof WeakSet !== 'undefined') {
    it('compares WeakSet objects by identity', function() {
      var s = new WeakSet([]);
      eq(R.equals(s, s), true);
      eq(R.equals(s, new WeakSet([])), false);
    });
  }
  it('dispatches to `equals` method recursively', function() {
    function Left(x) { this.value = x; }
    Left.prototype.equals = function(x) {
      return x instanceof Left && R.equals(x.value, this.value);
    };
    function Right(x) { this.value = x; }
    Right.prototype.equals = function(x) {
      return x instanceof Right && R.equals(x.value, this.value);
    };
    eq(R.equals(new Left([42]), new Left([42])), true);
    eq(R.equals(new Left([42]), new Left([43])), false);
    eq(R.equals(new Left(42), {value: 42}), false);
    eq(R.equals({value: 42}, new Left(42)), false);
    eq(R.equals(new Left(42), new Right(42)), false);
    eq(R.equals(new Right(42), new Left(42)), false);
    eq(R.equals([new Left(42)], [new Left(42)]), true);
    eq(R.equals([new Left(42)], [new Right(42)]), false);
    eq(R.equals([new Right(42)], [new Left(42)]), false);
    eq(R.equals([new Right(42)], [new Right(42)]), true);
  });
});

filter

Reason for failing: ramda dispatches to filter method of object

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('filter', function() {
  var even = function(x) {return x % 2 === 0;};
  it('dispatches to passed-in non-Array object with a `filter` method', function() {
    var f = {filter: function(f) { return f('called f.filter'); }};
    eq(R.filter(function(s) { return s; }, f), 'called f.filter');
  });
});

flip

Reason for failing: rambda flip work only for functions with two arguments

var jsv = require('jsverify');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
var funcN = require('./shared/funcN');
describe('flip', function() {
  it('returns a function which inverts the first two arguments to the supplied function', function() {
    var f = function(a, b, c) {return a + ' ' + b + ' ' + c;};
    var g = R.flip(f);
    eq(f('a', 'b', 'c'), 'a b c');
    eq(g('a', 'b', 'c'), 'b a c');
  });
  it('returns a curried function', function() {
    var f = function(a, b, c) {return a + ' ' + b + ' ' + c;};
    var g = R.flip(f)('a');
    eq(g('b', 'c'), 'b a c');
  });
  it('returns a function with the correct arity', function() {
    var f2 = function(a, b) {return a + ' ' + b;};
    var f3 = function(a, b, c) {return a + ' ' + b + ' ' + c;};
    eq(R.flip(f2).length, 2);
    eq(R.flip(f3).length, 3);
  });
});
describe('flip properties', function() {
  jsv.property('inverts first two arguments', funcN(3), jsv.json, jsv.json, jsv.json, function(f, a, b, c) {
    var g = R.flip(f);
    return R.equals(f(a, b, c), g(b, a, c));
  });
});

forEach

Reason for failing: ramda method dispatches to forEach method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('forEach', function() {
  var list = [{x: 1, y: 2}, {x: 100, y: 200}, {x: 300, y: 400}, {x: 234, y: 345}];
  it('dispatches to `forEach` method', function() {
    var dispatched = false;
    var fn = function() {};
    function DummyList() {}
    DummyList.prototype.forEach = function(callback) {
      dispatched = true;
      eq(callback, fn);
    };
    R.forEach(fn, new DummyList());
    eq(dispatched, true);
  });
});

groupBy

Reason for failing: ramda support transforms

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
var _isTransformer = require('rambda/internal/_isTransformer');

describe('groupBy', function() {
  it('dispatches on transformer objects in list position', function() {
    var byType = R.prop('type');
    var xf = {
      '@@transducer/init': function() { return {}; },
      '@@transducer/result': function(x) { return x; },
      '@@transducer/step': R.mergeRight
    };
    eq(_isTransformer(R.groupBy(byType, xf)), true);
  });
});

groupWith

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('groupWith', function() {
  it('splits the list into groups according to the grouping function', function() {
    eq(R.groupWith(R.equals, [1, 2, 2, 3]), [[1], [2, 2], [3]]);
    eq(R.groupWith(R.equals, [1, 1, 1, 1]), [[1, 1, 1, 1]]);
    eq(R.groupWith(R.equals, [1, 2, 3, 4]), [[1], [2], [3], [4]]);
  });
  it('can be turned into the original list through concatenation', function() {
    var list = [1, 1, 2, 3, 4, 4, 5, 5];
    eq(R.unnest(R.groupWith(R.equals, list)), list);
    eq(R.unnest(R.groupWith(R.complement(R.equals), list)), list);
    eq(R.unnest(R.groupWith(R.T, list)), list);
    eq(R.unnest(R.groupWith(R.F, list)), list);
  });
  it('also works on strings', function() {
    eq(R.groupWith(R.equals)('Mississippi'), ['M','i','ss','i','ss','i','pp','i']);
  });
});

has

Reason for failing: rambda does check properties from the prototype chain

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('has', function() {
  var fred = {name: 'Fred', age: 23};
  var anon = {age: 99};
  it('does not check properties from the prototype chain', function() {
    var Person = function() {};
    Person.prototype.age = function() {};
    var bob = new Person();
    eq(R.has('age', bob), false);
  });
});

ifElse

Reason for failing: rambda doesn't return a curried function

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('ifElse', function() {
  var t = function(a) { return a + 1; };
  var identity = function(a) { return a; };
  var isArray = function(a) { return Object.prototype.toString.call(a) === '[object Array]'; };
  it('returns a function whose arity equals the max arity of the three arguments to `ifElse`', function() {
    function a0() { return 0; }
    function a1(x) { return x; }
    function a2(x, y) { return x + y; }
    eq(R.ifElse(a0, a1, a2).length, 2);
    eq(R.ifElse(a0, a2, a1).length, 2);
    eq(R.ifElse(a1, a0, a2).length, 2);
    eq(R.ifElse(a1, a2, a0).length, 2);
    eq(R.ifElse(a2, a0, a1).length, 2);
    eq(R.ifElse(a2, a1, a0).length, 2);
  });
  it('returns a curried function', function() {
    var v = function(a) { return typeof a === 'number'; };
    var ifIsNumber = R.ifElse(v);
    eq(ifIsNumber(t, identity)(15), 16);
    eq(ifIsNumber(t, identity)('hello'), 'hello');
    var fn = R.ifElse(R.gt, R.subtract, R.add);
    eq(fn(2)(7), 9);
    eq(fn(2, 7), 9);
    eq(fn(7)(2), 5);
    eq(fn(7, 2), 5);
  });
});

includes

Reason for failing: ramda method pass to equals method if available

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('includes', function() {
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.includes(0, [-0]), false);
    eq(R.includes(-0, [0]), false);
    eq(R.includes(NaN, [NaN]), true);
    eq(R.includes(new Just([42]), [new Just([42])]), true);
  });
});

indexBy

Reason for failing: ramda method can act as a transducer

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('indexBy', function() {
  it('can act as a transducer', function() {
    var list = [{id: 'xyz', title: 'A'}, {id: 'abc', title: 'B'}];
    var transducer = R.compose(
      R.indexBy(R.prop('id')),
      R.map(R.pipe(
        R.adjust(0, R.toUpper),
        R.adjust(1, R.omit(['id']))
      )));
    var result = R.into({}, transducer, list);
    eq(result, {ABC: {title: 'B'}, XYZ: {title: 'A'}});
  });
});

indexOf

Reason for failing: ramda method dispatches to indexOf method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('indexOf', function() {
  var input = [1, 2, 3, 4, 5];
  var list = [1, 2, 3];
  list[-2] = 4; // Throw a wrench in the gears by assigning a non-valid array index as object property.
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.indexOf(0, [-0]), -1);
    eq(R.indexOf(-0, [0]), -1);
    eq(R.indexOf(NaN, [NaN]), 0);
    eq(R.indexOf(new Just([42]), [new Just([42])]), 0);
  });
  it('dispatches to `indexOf` method', function() {
    function Empty() {}
    Empty.prototype.indexOf = R.always(-1);
    function List(head, tail) {
      this.head = head;
      this.tail = tail;
    }
    List.prototype.indexOf = function(x) {
      var idx = this.tail.indexOf(x);
      return this.head === x ? 0 : idx >= 0 ? 1 + idx : -1;
    };
    var list = new List('b',
      new List('a',
        new List('n',
          new List('a',
            new List('n',
              new List('a',
                new Empty()
              )
            )
          )
        )
      )
    );
    eq(R.indexOf('a', 'banana'), 1);
    eq(R.indexOf('x', 'banana'), -1);
    eq(R.indexOf('a', list), 1);
    eq(R.indexOf('x', list), -1);
  });
});

intersection

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('intersection', function() {
  var M = [1, 2, 3, 4];
  var M2 = [1, 2, 3, 4, 1, 2, 3, 4];
  var N = [3, 4, 5, 6];
  var N2 = [3, 3, 4, 4, 5, 5, 6, 6];
  it('does not allow duplicates in the output even if the input lists had duplicates', function() {
    eq(R.intersection(M2, N2), [3, 4]);
  });
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.intersection([0], [-0]).length, 0);
    eq(R.intersection([-0], [0]).length, 0);
    eq(R.intersection([NaN], [NaN]).length, 1);
    eq(R.intersection([new Just([42])], [new Just([42])]).length, 1);
  });
});

intersperse

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('intersperse', function() {
  it('dispatches', function() {
    var obj = {intersperse: function(x) { return 'override ' + x; }};
    eq(R.intersperse('x', obj), 'override x');
  });
});

isEmpty

Reason for failing: ramda supports typed arrays

var R = require('rambda');
var eq = require('./shared/eq');

describe('isEmpty', function() {
  const a = 1
  it('returns true for empty typed array', function() {
    eq(R.isEmpty(Uint8Array.from('')), true);
    eq(R.isEmpty(Float32Array.from('')), true);
    eq(R.isEmpty(new Float32Array([])), true);
    eq(R.isEmpty(Uint8Array.from('1')), false);
    eq(R.isEmpty(Float32Array.from('1')), false);
    eq(R.isEmpty(new Float32Array([1])), false);
  });
});

keys

Reason for failing: ramda method works for primitives

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('keys', function() {
  var obj = {a: 100, b: [1, 2, 3], c: {x: 200, y: 300}, d: 'D', e: null, f: undefined};
  function C() { this.a = 100; this.b = 200; }
  C.prototype.x = function() { return 'x'; };
  C.prototype.y = 'y';
  var cobj = new C();
  it('works for primitives', function() {
    eq(R.keys(null), []);
    eq(R.keys(undefined), []);
    eq(R.keys(55), []);
    eq(R.keys('foo'), []);
    eq(R.keys(true), []);
    eq(R.keys(false), []);
    eq(R.keys(NaN), []);
    eq(R.keys(Infinity), []);
    eq(R.keys([]), []);
  });
});

lastIndexOf

Reason for failing: ramda method dispatches to lastIndexOf method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('lastIndexOf', function() {
  var input = [1, 2, 3, 4, 5, 1];
  var list = ['a', 1, 'a'];
  list[-2] = 'a'; // Throw a wrench in the gears by assigning a non-valid array index as object property.
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.lastIndexOf(0, [-0]), -1);
    eq(R.lastIndexOf(-0, [0]), -1);
    eq(R.lastIndexOf(NaN, [NaN]), 0);
    eq(R.lastIndexOf(new Just([42]), [new Just([42])]), 0);
  });
  it('dispatches to `lastIndexOf` method', function() {
    function Empty() {}
    Empty.prototype.lastIndexOf = R.always(-1);
    function List(head, tail) {
      this.head = head;
      this.tail = tail;
    }
    List.prototype.lastIndexOf = function(x) {
      var idx = this.tail.lastIndexOf(x);
      return idx >= 0 ? 1 + idx : this.head === x ? 0 : -1;
    };
    var list = new List('b',
      new List('a',
        new List('n',
          new List('a',
            new List('n',
              new List('a',
                new Empty()
              )
            )
          )
        )
      )
    );
    eq(R.lastIndexOf('a', 'banana'), 5);
    eq(R.lastIndexOf('x', 'banana'), -1);
    eq(R.lastIndexOf('a', list), 5);
    eq(R.lastIndexOf('x', list), -1);
  });
  it('finds function, compared by identity', function() {
    var f = function() {};
    var g = function() {};
    var list = [g, f, g, f];
    eq(R.lastIndexOf(f, list), 3);
  });
});

length

Reason for failing: ramda method supports object with length method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('length', function() {
  it('returns NaN for length property of unexpected type', function() {
    eq(R.identical(NaN, R.length({length: ''})), true);
    eq(R.identical(NaN, R.length({length: '1.23'})), true);
    eq(R.identical(NaN, R.length({length: null})), true);
    eq(R.identical(NaN, R.length({length: undefined})), true);
    eq(R.identical(NaN, R.length({})), true);
  });
});

mean

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('mean', function() {
  it('handles array-like object', function() {
    eq(R.mean((function() { return arguments; })(1, 2, 3)), 2);
  });
});

partial

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('partial', function() {
  var disc = function(a, b, c) { // note disc(3, 7, 4) => 1
    return b * b - 4 * a * c;
  };
  it('caches the initially supplied arguments', function() {
    var f = R.partial(disc, [3]);
    eq(f(7, 4), 1);
    var g = R.partial(disc, [3, 7]);
    eq(g(4), 1);
  });
  it('correctly reports the arity of the new function', function() {
    var f = R.partial(disc, [3]);
    eq(f.length, 2);
    var g = R.partial(disc, [3, 7]);
    eq(g.length, 1);
  });
});

path

Reason for failing: ramda method supports negative indices

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('path', function() {
  var deepObject = {a: {b: {c: 'c'}}, falseVal: false, nullVal: null, undefinedVal: undefined, arrayVal: ['arr']};
  it('takes a path that contains negative indices into arrays', function() {
    eq(R.path(['x', -2], {x: ['a', 'b', 'c', 'd']}), 'c');
    eq(R.path([-1, 'y'], [{x: 1, y: 99}, {x: 2, y: 98}, {x: 3, y: 97}]), 97);
  });
});

pipe

Reason for failing: ramda passes context to functions | rambda composed functions have no length

var assert = require('assert');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('pipe', function() {
  it('performs left-to-right function composition', function() {
    //  f :: (String, Number?) -> ([Number] -> [Number])
    var f = R.pipe(parseInt, R.multiply, R.map);
    eq(f.length, 2);
    eq(f('10')([1, 2, 3]), [10, 20, 30]);
    eq(f('10', 2)([1, 2, 3]), [2, 4, 6]);
  });
  it('passes context to functions', function() {
    function x(val) {
      return this.x * val;
    }
    function y(val) {
      return this.y * val;
    }
    function z(val) {
      return this.z * val;
    }
    var context = {
      a: R.pipe(x, y, z),
      x: 4,
      y: 2,
      z: 1
    };
    eq(context.a(5), 40);
  });
  it('can be applied to one argument', function() {
    var f = function(a, b, c) { return [a, b, c]; };
    var g = R.pipe(f);
    eq(g.length, 3);
    eq(g(1, 2, 3), [1, 2, 3]);
  });
});

pluck

Reason for failing: ramda method behaves as a transducer

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('pluck', function() {
  var people = [
    {name: 'Fred', age: 23},
    {name: 'Wilma', age: 21},
    {name: 'Pebbles', age: 2}
  ];
  it('behaves as a transducer when given a transducer in list position', function() {
    var numbers = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];
    var transducer = R.compose(R.pluck('a'), R.map(R.add(1)), R.take(2));
    eq(R.transduce(transducer, R.flip(R.append), [], numbers), [2, 3]);
  });
});

propEq

Reason for failing: ramda method pass to equals method if available

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('propEq', function() {
  var obj1 = {name: 'Abby', age: 7, hair: 'blond'};
  var obj2 = {name: 'Fred', age: 12, hair: 'brown'};
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.propEq('value', 0, {value: -0}), false);
    eq(R.propEq('value', -0, {value: 0}), false);
    eq(R.propEq('value', NaN, {value: NaN}), true);
    eq(R.propEq('value', new Just([42]), {value: new Just([42])}), true);
  });
});

reduce

Reason for failing: rambda doesn't have R.reduced method | ramda method pass to reduce method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('reduce', function() {
  var add = function(a, b) {return a + b;};
  var mult = function(a, b) {return a * b;};
  it('Prefers the use of the iterator of an object over reduce (and handles short-circuits)', function() {
    var symIterator = (typeof Symbol !== 'undefined') ? Symbol.iterator : '@@iterator';
    function Reducible(arr) {
      this.arr = arr;
    }
    Reducible.prototype.reduce = function(f, init) {
      var acc = init;
      for (var i = 0; i < this.arr.length; i += 1) {
        acc = f(acc, this.arr[i]);
      }
      return acc;
    };
    Reducible.prototype[symIterator] = function() {
      var a = this.arr;
      return {
        _pos: 0,
        next: function() {
          if (this._pos < a.length) {
            var v = a[this._pos];
            this._pos += 1;
            return {
              value: v,
              done: false
            };
          } else {
            return {
              done: true
            };
          }
        }
      };
    };
    var xf = R.take(2);
    var apendingT = { };
    apendingT['@@transducer/result'] = R.identity;
    apendingT['@@transducer/step'] = R.flip(R.append);
    var rfn = xf(apendingT);
    var list = new Reducible([1, 2, 3, 4, 5, 6]);
    eq(R.reduce(rfn, [], list), [1, 2]);
  });
  it('short circuits with reduced', function() {
    var addWithMaxOf10 = function(acc, val) {return acc + val > 10 ? R.reduced(acc) : acc + val;};
    eq(R.reduce(addWithMaxOf10, 0, [1, 2, 3, 4]), 10);
    eq(R.reduce(addWithMaxOf10, 0, [2, 4, 6, 8]), 6);
  });
});

reject

Reason for failing: ramda method dispatches to filter method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('reject', function() {
  var even = function(x) {return x % 2 === 0;};
  it('dispatches to `filter` method', function() {
    function Nothing() {}
    Nothing.value = new Nothing();
    Nothing.prototype.filter = function() {
      return this;
    };
    function Just(x) { this.value = x; }
    Just.prototype.filter = function(pred) {
      return pred(this.value) ? this : Nothing.value;
    };
    var m = new Just(42);
    eq(R.filter(R.T, m), m);
    eq(R.filter(R.F, m), Nothing.value);
    eq(R.reject(R.T, m), Nothing.value);
    eq(R.reject(R.F, m), m);
  });
});

slice

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('slice', function() {
  it('handles array-like object', function() {
    var args = (function() { return arguments; }(1, 2, 3, 4, 5));
    eq(R.slice(1, 4, args), [2, 3, 4]);
  });
});

sortBy

Reason for failing: ramda works with array-like objects

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

var albums = [
  {title: 'Art of the Fugue', artist: 'Glenn Gould', genre: 'Baroque'},
  {title: 'A Farewell to Kings', artist: 'Rush', genre: 'Rock'},
  {title: 'Timeout', artist: 'Dave Brubeck Quartet', genre: 'Jazz'},
  {title: 'Fly By Night', artist: 'Rush', genre: 'Rock'},
  {title: 'Goldberg Variations', artist: 'Daniel Barenboim', genre: 'Baroque'},
  {title: 'New World Symphony', artist: 'Leonard Bernstein', genre: 'Romantic'},
  {title: 'Romance with the Unseen', artist: 'Don Byron', genre: 'Jazz'},
  {title: 'Somewhere In Time', artist: 'Iron Maiden', genre: 'Metal'},
  {title: 'In Times of Desparation', artist: 'Danny Holt', genre: 'Modern'},
  {title: 'Evita', artist: 'Various', genre: 'Broadway'},
  {title: 'Five Leaves Left', artist: 'Nick Drake', genre: 'Folk'},
  {title: 'The Magic Flute', artist: 'John Eliot Gardiner', genre: 'Classical'}
];
describe('sortBy', function() {
  it('sorts array-like object', function() {
    var args = (function() { return arguments; }('c', 'a', 'b'));
    var result = R.sortBy(R.identity, args);
    eq(result[0], 'a');
    eq(result[1], 'b');
    eq(result[2], 'c');
  });
});

startsWith

Reason for failing: rambda doesn't support arrays

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('startsWith', function() {
  it('should return true when an array starts with the provided value', function() {
    eq(R.startsWith(['a'], ['a', 'b', 'c']), true);
  });
  it('should return true when an array starts with the provided values', function() {
    eq(R.startsWith(['a', 'b'], ['a', 'b', 'c']), true);
  });
  it('should return false when an array does not start with the provided value', function() {
    eq(R.startsWith(['b'], ['a', 'b', 'c']), false);
  });
  it('should return false when an array does not start with the provided values', function() {
    eq(R.startsWith(['b', 'c'], ['a', 'b', 'c']), false);
  });
});

symmetricDifference

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('symmetricDifference', function() {
  var M = [1, 2, 3, 4];
  var M2 = [1, 2, 3, 4, 1, 2, 3, 4];
  var N = [3, 4, 5, 6];
  var N2 = [3, 3, 4, 4, 5, 5, 6, 6];
  var Z = [3, 4, 5, 6, 10];
  var Z2 = [1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8];
  it('does not allow duplicates in the output even if the input lists had duplicates', function() {
    eq(R.symmetricDifference(M2, N2), [1, 2, 5, 6]);
  });
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.symmetricDifference([0], [-0]).length, 2);
    eq(R.symmetricDifference([-0], [0]).length, 2);
    eq(R.symmetricDifference([NaN], [NaN]).length, 0);
    eq(R.symmetricDifference([new Just([42])], [new Just([42])]).length, 0);
  });
  it('will not create a "sparse" array', function() {
    eq(R.symmetricDifference(M2, [3]).length, 3);
  });
});

take

Reason for failing: rambda doesn't have 'R.into` method

var assert = require('assert');
var sinon = require('sinon');

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
describe('take', function() {
  it('handles zero correctly (#1224)', function() {
    eq(R.into([], R.take(0), [1, 2, 3]), []);
  });
  it('steps correct number of times', function() {
    var spy = sinon.spy();
    R.into([], R.compose(R.map(spy), R.take(2)), [1, 2, 3]);
    sinon.assert.calledTwice(spy);
  });
  it('transducer called for every member of list if `n` is < 0', function() {
    var spy = sinon.spy();
    R.into([], R.compose(R.map(spy), R.take(-1)), [1, 2, 3]);
    sinon.assert.calledThrice(spy);
  });
});

tap

Reason for failing: ramda can act as a transducer

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');
var listXf = require('./helpers/listXf');
var _curry2 = require('rambda/internal/_curry2');

describe('tap', function() {
  var pushToList = _curry2(function(lst, x) { lst.push(x); });
  it('can act as a transducer', function() {
    var sideEffect = [];
    var numbers = [1,2,3,4,5];
    var xf = R.compose(R.map(R.identity), R.tap(pushToList(sideEffect)));
    eq(R.into([], xf, numbers), numbers);
    eq(sideEffect, numbers);
  });
  it('dispatches to transformer objects', function() {
    var sideEffect = [];
    var pushToSideEffect = pushToList(sideEffect);
    eq(R.tap(pushToSideEffect, listXf), {
      f: pushToSideEffect,
      xf: listXf
    });
  });
});

toString

var assert = require('assert');

var R = require('../../../../../rambda/dist/rambda.js');
describe('toString', function() {
  it('returns the string representation of null', function() {
    assert.strictEqual(R.toString(null), 'null');
  });
  it('returns the string representation of undefined', function() {
    assert.strictEqual(R.toString(undefined), 'undefined');
  });
  it('returns the string representation of a number primitive', function() {
    assert.strictEqual(R.toString(0), '0');
    assert.strictEqual(R.toString(-0), '-0');
    assert.strictEqual(R.toString(1.23), '1.23');
    assert.strictEqual(R.toString(-1.23), '-1.23');
    assert.strictEqual(R.toString(1e+23), '1e+23');
    assert.strictEqual(R.toString(-1e+23), '-1e+23');
    assert.strictEqual(R.toString(1e-23), '1e-23');
    assert.strictEqual(R.toString(-1e-23), '-1e-23');
    assert.strictEqual(R.toString(Infinity), 'Infinity');
    assert.strictEqual(R.toString(-Infinity), '-Infinity');
    assert.strictEqual(R.toString(NaN), 'NaN');
  });
  it('returns the string representation of a string primitive', function() {
    assert.strictEqual(R.toString('abc'), '"abc"');
    assert.strictEqual(R.toString('x "y" z'), '"x \\"y\\" z"');
    assert.strictEqual(R.toString("' '"), '"\' \'"');
    assert.strictEqual(R.toString('" "'), '"\\" \\""');
    assert.strictEqual(R.toString('\b \b'), '"\\b \\b"');
    assert.strictEqual(R.toString('\f \f'), '"\\f \\f"');
    assert.strictEqual(R.toString('\n \n'), '"\\n \\n"');
    assert.strictEqual(R.toString('\r \r'), '"\\r \\r"');
    assert.strictEqual(R.toString('\t \t'), '"\\t \\t"');
    assert.strictEqual(R.toString('\v \v'), '"\\v \\v"');
    assert.strictEqual(R.toString('\0 \0'), '"\\0 \\0"');
    assert.strictEqual(R.toString('\\ \\'), '"\\\\ \\\\"');
  });
  it('returns the string representation of a Boolean object', function() {
    assert.strictEqual(R.toString(new Boolean(true)), 'new Boolean(true)');
    assert.strictEqual(R.toString(new Boolean(false)), 'new Boolean(false)');
  });
  it('returns the string representation of a Number object', function() {
    assert.strictEqual(R.toString(new Number(0)), 'new Number(0)');
    assert.strictEqual(R.toString(new Number(-0)), 'new Number(-0)');
  });
  it('returns the string representation of a String object', function() {
    assert.strictEqual(R.toString(new String('abc')), 'new String("abc")');
    assert.strictEqual(R.toString(new String('x "y" z')), 'new String("x \\"y\\" z")');
    assert.strictEqual(R.toString(new String("' '")), 'new String("\' \'")');
    assert.strictEqual(R.toString(new String('" "')), 'new String("\\" \\"")');
    assert.strictEqual(R.toString(new String('\b \b')), 'new String("\\b \\b")');
    assert.strictEqual(R.toString(new String('\f \f')), 'new String("\\f \\f")');
    assert.strictEqual(R.toString(new String('\n \n')), 'new String("\\n \\n")');
    assert.strictEqual(R.toString(new String('\r \r')), 'new String("\\r \\r")');
    assert.strictEqual(R.toString(new String('\t \t')), 'new String("\\t \\t")');
    assert.strictEqual(R.toString(new String('\v \v')), 'new String("\\v \\v")');
    assert.strictEqual(R.toString(new String('\0 \0')), 'new String("\\0 \\0")');
    assert.strictEqual(R.toString(new String('\\ \\')), 'new String("\\\\ \\\\")');
  });
  it('returns the string representation of a Date object', function() {
    assert.strictEqual(R.toString(new Date('2001-02-03T04:05:06.000Z')), 'new Date("2001-02-03T04:05:06.000Z")');
    assert.strictEqual(R.toString(new Date('XXX')), 'new Date(NaN)');
  });
  it('returns the string representation of an array', function() {
    assert.strictEqual(R.toString([]), '[]');
    assert.strictEqual(R.toString([1, 2, 3]), '[1, 2, 3]');
    assert.strictEqual(R.toString([1, [2, [3]]]), '[1, [2, [3]]]');
    assert.strictEqual(R.toString(['x', 'y']), '["x", "y"]');
  });
  it('returns the string representation of an array with non-numeric property names', function() {
    var xs = [1, 2, 3];
    xs.foo = 0;
    xs.bar = 0;
    xs.baz = 0;
    assert.strictEqual(R.toString(xs), '[1, 2, 3, "bar": 0, "baz": 0, "foo": 0]');
  });
  it('returns the string representation of an arguments object', function() {
    assert.strictEqual(R.toString((function() { return arguments; })()), '(function() { return arguments; }())');
    assert.strictEqual(R.toString((function() { return arguments; })(1, 2, 3)), '(function() { return arguments; }(1, 2, 3))');
    assert.strictEqual(R.toString((function() { return arguments; })(['x', 'y'])), '(function() { return arguments; }(["x", "y"]))');
  });
  it('returns the string representation of a plain object', function() {
    assert.strictEqual(R.toString({}), '{}');
    assert.strictEqual(R.toString({foo: 1, bar: 2, baz: 3}), '{"bar": 2, "baz": 3, "foo": 1}');
    assert.strictEqual(R.toString({'"quoted"': true}), '{"\\"quoted\\"": true}');
    assert.strictEqual(R.toString({a: {b: {c: {}}}}), '{"a": {"b": {"c": {}}}}');
  });
  it('treats instance without custom `toString` method as plain object', function() {
    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    assert.strictEqual(R.toString(new Point(1, 2)), '{"x": 1, "y": 2}');
  });
  it('dispatches to custom `toString` method', function() {
    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    Point.prototype.toString = function() {
      return 'new Point(' + this.x + ', ' + this.y + ')';
    };
    assert.strictEqual(R.toString(new Point(1, 2)), 'new Point(1, 2)');
    function Just(x) {
      if (!(this instanceof Just)) {
        return new Just(x);
      }
      this.value = x;
    }
    Just.prototype.toString = function() {
      return 'Just(' + R.toString(this.value) + ')';
    };
    assert.strictEqual(R.toString(Just(42)), 'Just(42)');
    assert.strictEqual(R.toString(Just([1, 2, 3])), 'Just([1, 2, 3])');
    assert.strictEqual(R.toString(Just(Just(Just('')))), 'Just(Just(Just("")))');
    assert.strictEqual(R.toString({toString: R.always('x')}), 'x');
  });
  it('handles object with no `toString` method', function() {
    if (typeof Object.create === 'function') {
      var a = Object.create(null);
      var b = Object.create(null); b.x = 1; b.y = 2;
      assert.strictEqual(R.toString(a), '{}');
      assert.strictEqual(R.toString(b), '{"x": 1, "y": 2}');
    }
  });
  it('handles circular references', function() {
    var a = [];
    a[0] = a;
    assert.strictEqual(R.toString(a), '[<Circular>]');
    var o = {};
    o.o = o;
    assert.strictEqual(R.toString(o), '{"o": <Circular>}');
    var b = ['bee'];
    var c = ['see'];
    b[1] = c;
    c[1] = b;
    assert.strictEqual(R.toString(b), '["bee", ["see", <Circular>]]');
    assert.strictEqual(R.toString(c), '["see", ["bee", <Circular>]]');
    var p = {};
    var q = {};
    p.q = q;
    q.p = p;
    assert.strictEqual(R.toString(p), '{"q": {"p": <Circular>}}');
    assert.strictEqual(R.toString(q), '{"p": {"q": <Circular>}}');
    var x = [];
    var y = {};
    x[0] = y;
    y.x = x;
    assert.strictEqual(R.toString(x), '[{"x": <Circular>}]');
    assert.strictEqual(R.toString(y), '{"x": [<Circular>]}');
  });
});

trim

Reason for failing: ramda trims all ES5 whitespace

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('trim', function() {
  var test = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFFHello, World!\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
  it('trims all ES5 whitespace', function() {
    eq(R.trim(test), 'Hello, World!');
    eq(R.trim(test).length, 13);
  });
  if (typeof String.prototype.trim !== 'function') {
    it('falls back to a shim if String.prototype.trim is not present', function() {
      eq(R.trim('   xyz  '), 'xyz');
      eq(R.trim(test), 'Hello, World!');
      eq(R.trim(test).length, 13);
      eq(R.trim('\u200b'), '\u200b');
      eq(R.trim('\u200b').length, 1);
    });
  }
});

type

Reason for failing: ramda returns 'Number' type to NaN input, while rambda returns 'NaN'

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('type', function() {
  // it('"Arguments" if given an arguments object', function() {
  //   var args = (function() { return arguments; }());
  //   eq(R.type(args), 'Arguments');
  // });
  it('"Number" if given the NaN value', function() {
    eq(R.type(NaN), 'Number');
  });
});

uniq

Reason for failing: ramda pass to uniq method | ramda method uses reference equality for functions

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('uniq', function() {
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.uniq([-0, -0]).length, 1);
    eq(R.uniq([0, -0]).length, 2);
    eq(R.uniq([NaN, NaN]).length, 1);
    eq(R.uniq([[1], [1]]).length, 1);
    eq(R.uniq([new Just([42]), new Just([42])]).length, 1);
  it('handles null and undefined elements', function() {
    eq(R.uniq([void 0, null, void 0, null]), [void 0, null]);
  it('uses reference equality for functions', function() {
    eq(R.uniq([R.add, R.identity, R.add, R.identity, R.add, R.identity]).length, 2);
});

update

Reason for failing: ramda accepts an array-like object

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('update', function() {
  it('accepts an array-like object', function() {
    function args() {
      return arguments;
    }
    eq(R.update(2, 4, args(0, 1, 2, 3)), [0, 1, 4, 3]);
  });
});

without

Reason for failing: ramda method act as a transducer | ramda method pass to equals method

var R = require('../../../../../rambda/dist/rambda.js');
var eq = require('./shared/eq');

describe('without', function() {
  it('can act as a transducer', function() {
    eq(R.into([], R.without([1]), [1]), []);
  });
  it('has R.equals semantics', function() {
    function Just(x) { this.value = x; }
    Just.prototype.equals = function(x) {
      return x instanceof Just && R.equals(x.value, this.value);
    };
    eq(R.without([0], [-0]).length, 1);
    eq(R.without([-0], [0]).length, 1);
    eq(R.without([NaN], [NaN]).length, 0);
    eq(R.without([[1]], [[1]]).length, 0);
    eq(R.without([new Just([42])], [new Just([42])]).length, 0);
  });
});

You can see them as separate files in ./files/failing_ramda_tests directory

API


add

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

R.add(2, 3) // =>  5
R.add tests
import { add } from './add'
import R from 'ramda'

test('with number', () => {
  expect(add(2, 3)).toEqual(5)
  expect(add(7)(10)).toEqual(17)
})

test('with string returns NaN', () => {
  expect(R.add('foo', 'bar')).toEqual(Number('foo'))
})

test('ramda specs', () => {
  expect(add('1', '2'),).toEqual(3)
  expect(add(1, '2'),).toEqual(3)
  expect(add(true, false),).toEqual(1)
  expect(add(null, null),).toEqual(0)
  expect(add(undefined, undefined),).toEqual(NaN)
  expect(add(new Date(1), new Date(2)),).toEqual(3)
})
R.add source
export function add(a, b){
  if (arguments.length === 1) return _b => add(a, _b)

  return Number(a) + Number(b)
}

Try in REPL


adjust

adjust(replaceFn: Function, i: number, arr: T[]): T[]

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

R.adjust(
  a => a + 1,
  0,
  [0, 100]
) // => [1, 100]
R.adjust tests
import { add } from './add'
import { adjust } from './adjust'

const expected = [ 0, 11, 2 ]

test('without curring', () => {
  expect(adjust(1, add(10), [ 0, 1, 2 ])).toEqual(expected)
})

test('with curring type 1 1 1', () => {
  expect(adjust(1)(add(10))([ 0, 1, 2 ])).toEqual(expected)
})

test('with curring type 1 2', () => {
  expect(adjust(1)(add(10), [ 0, 1, 2 ])).toEqual(expected)
})

test('with curring type 2 1', () => {
  expect(adjust(1, add(10))([ 0, 1, 2 ])).toEqual(expected)
})

test('with negative index', () => {
  expect(adjust(-2, add(10), [ 0, 1, 2 ])).toEqual(expected)
})

test('when index is out of bounds', () => {
  const list = [ 0, 1, 2, 3 ]
  expect(adjust(4, add(1), list),).toEqual(list)
  expect(adjust(-5, add(1), list),).toEqual(list)
})
R.adjust source
import { curry } from './curry'

export const adjust = curry(adjustFn)

Try in REPL


all

all(fn: Function, arr: T[]): boolean

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

const arr = [ 0, 1, 2, 3, 4 ]
const fn = x => x > -1

const result = R.all(fn, arr)
// => true
R.all tests
import { all } from './all'

const numArr = [ 0, 1, 2, 3, 4 ]

test('when true', () => {
  const fn = x => x > -1

  expect(all(fn)(numArr)).toBeTruthy()
})

test('when false', () => {
  const fn = x => x > 2

  expect(all(fn, numArr)).toBeFalsy()
})

test('pass index as second argument', () => {
  const indexes = []
  const fn = (x, i) => {
    indexes.push(i)

    return x > 5
  }
  all(fn, [ 10, 12, 14 ])

  expect(indexes).toEqual([ 0, 1, 2 ])
})
R.all source
export function all(fn, list){
  if (arguments.length === 1) return _list => all(fn, _list)

  for (let i = 0; i < list.length; i++){
    if (!fn(list[ i ], i))
      return false
  }

  return true
}

Try in REPL


allPass

allPass(rules: Function[], input: any): boolean

It returns true, if all functions of rules return true, when input is their argument.

const input = {
  a : 1,
  b : 2,
}
const rules = [
  x => x.a === 1,
  x => x.b === 2,
]
const result = R.allPass(rules, input) // => true
R.allPass tests
import { allPass } from './allPass'

test('', () => {
  const rules = [
    x => typeof x === 'number',
    x => x > 10,
    x => x * 7 < 100,
  ]

  expect(allPass(rules)(11)).toBeTruthy()

  expect(allPass(rules)(undefined)).toBeFalsy()
})

test('when returns true', () => {
  const conditionArr = [ val => val.a === 1, val => val.b === 2 ]

  expect(
    allPass(conditionArr)({
      a : 1,
      b : 2,
    })
  ).toBeTruthy()
})

test('when returns false', () => {
  const conditionArr = [ val => val.a === 1, val => val.b === 3 ]

  expect(
    allPass(conditionArr)({
      a : 1,
      b : 2,
    })
  ).toBeFalsy()
})
R.allPass source
export function allPass(predicates){
  return input => {
    let counter = 0
    while (counter < predicates.length){
      if (!predicates[ counter ](input)){
        return false
      }
      counter++
    }

    return true
  }
}

Try in REPL


always

always(x: any): Function

It returns function that always returns x.

const fn = R.always(7)

console.log(fn())// => 7
R.always tests
import { always } from './always'

test('', () => {
  const fn = always(7)

  expect(fn()).toEqual(7)
  expect(fn()).toEqual(7)
})
R.always source
export function always(val){
  return () => val
}

Try in REPL


any

any(condition: Function, arr: T[]): boolean

It returns true, if at least one member of arr returns true, when passed to the condition function.

R.any(a => a * a > 8)([1, 2, 3])
// => true
R.any tests
import { any } from './any'

const arr = [ 1, 2 ]

test('no curry', () => {
  expect(any(val => val < 0, arr)).toBeFalsy()
})

test('with curry', () => {
  expect(any(val => val < 2)(arr)).toBeTruthy()
})

test('passes index to predicate', () => {
  any((x, i) => {
    expect(typeof x).toBe('string')
    expect(typeof i).toBe('number')
  })([ 'foo', 'bar' ])
})
R.any source
export function any(fn, list){
  if (arguments.length === 1) return _list => any(fn, _list)

  let counter = 0
  while (counter < list.length){
    if (fn(list[ counter ], counter)){
      return true
    }
    counter++
  }

  return false
}

Try in REPL


anyPass

anyPass(conditions: Function[]): Function

const isBig = a => a > 20
const isOdd = a => a % 2 === 1

const result = R.anyPass(
  [isBig, isOdd]
)(11)
// => true
R.anyPass tests
import { anyPass } from './anyPass'

test('happy', () => {
  const rules = [ x => typeof x === 'string', x => x > 10 ]
  const predicate = anyPass(rules)
  expect(predicate('foo')).toBeTruthy()
  expect(predicate(6)).toBeFalsy()
})

test('', () => {
  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 + curry', () => {
  const conditionArr = [ val => val.a === 2, val => val.b === 3 ]

  expect(anyPass(conditionArr)(obj)).toBeFalsy()
})

test('happy', () => {
  expect(anyPass([])(3)).toEqual(false)
})
R.anyPass source
export function anyPass(predicates){
  return input => {
    let counter = 0
    while (counter < predicates.length){
      if (predicates[ counter ](input)){
        return true
      }
      counter++
    }

    return false
  }
}

Try in REPL


append

append(valueToAppend: T, arr: T[]): T[]

R.append(
  'foo',
  ['bar', 'baz']
) // => ['bar', 'baz', 'foo']
R.append tests
import { append } from './append'
import { compose } from './compose'
import { flatten } from './flatten'
import { map } from './map'

test('with strings', () => {
  expect(append('o', 'fo')).toEqual('foo')
})

test('with arrays', () => {
  expect(append('tests', [ 'write', 'more' ])).toEqual([
    'write',
    'more',
    'tests',
  ])
})

test('append to empty array', () => {
  expect(append('tests', [])).toEqual([ 'tests' ])
})

test('', () => {
  const result = compose(
    flatten,
    map(append(0))
  )([ [ 1 ], [ 2 ], [ 3 ] ])
  expect(result).toEqual([ 1, 0, 2, 0, 3, 0 ])
})

test('should not modify arguments', () => {
  const a = [ 1, 2, 3 ]
  const b = append(4, a)

  expect(a).toEqual([ 1, 2, 3 ])
  expect(b).toEqual([ 1, 2, 3, 4 ])
})
R.append source
export function append(el, list){
  if (arguments.length === 1) return _list => append(el, _list)

  if (typeof list === 'string') return `${ list }${ el }`

  const clone = list.slice()
  clone.push(el)

  return clone
}

Try in REPL


assoc

assoc(prop: any, value: any, obj: object): object

Makes a shallow clone of obj, setting or overriding the property prop with the value value. Note that this copies and flattens prototype properties onto the new object as well. All non-primitive properties are copied by reference.

R.assoc('c', 3, {a: 1, b: 2})
//=> {a: 1, b: 2, c: 3}
R.assoc tests
import { assoc } from './assoc'

test('adds a key to an empty object', () => {
  expect(assoc('a', 1, {})).toEqual({ a : 1 })
})

test('adds a key to a non-empty object', () => {
  expect(assoc('b', 2, { a : 1 })).toEqual({
    a : 1,
    b : 2,
  })
})

test('adds a key to a non-empty object - curry case 1', () => {
  expect(assoc('b', 2)({ a : 1 })).toEqual({
    a : 1,
    b : 2,
  })
})

test('adds a key to a non-empty object - curry case 2', () => {
  expect(assoc('b')(2, { a : 1 })).toEqual({
    a : 1,
    b : 2,
  })
})

test('adds a key to a non-empty object - curry case 3', () => {
  const result = assoc('b')(2)({ a : 1 })

  expect(result).toEqual({
    a : 1,
    b : 2,
  })
})

test('changes an existing key', () => {
  expect(assoc('a', 2, { a : 1 })).toEqual({ a : 2 })
})

test('undefined is considered an empty object', () => {
  expect(assoc('a', 1, undefined)).toEqual({ a : 1 })
})

test('null is considered an empty object', () => {
  expect(assoc('a', 1, null)).toEqual({ a : 1 })
})

test('value can be null', () => {
  expect(assoc('a', null, null)).toEqual({ a : null })
})

test('value can be undefined', () => {
  expect(assoc('a', undefined, null)).toEqual({ a : undefined })
})

test('assignment is shallow', () => {
  expect(assoc('a', { b : 2 }, { a : { c : 3 } })).toEqual({ a : { b : 2 } })
})
R.assoc source
import { curry } from './curry'

export const assoc = curry(assocFn)

Try in REPL


both

both(firstCondition: Function, secondCondition: Function, input: any): boolean

It returns true, if both function firstCondition and function secondCondition return true, when input is their argument.

const fn = R.both(
  a => a > 10,
  a => a < 20
)
console.log(fn(15)) //=> true
console.log(fn(30)) //=> false
R.both tests
import { both } from './both'

const firstFn = val => val > 0
const secondFn = val => val < 10

test('with curry', () => {
  expect(both(firstFn)(secondFn)(17)).toBeFalsy()
})

test('without curry', () => {
  expect(both(firstFn, secondFn)(7)).toBeTruthy()
})

test('with multiple inputs', () => {
  const between = function(a, b, c){ return a < b && b < c }
  const total20 = function(a, b, c){ return a + b + c === 20 }
  const fn = both(between, total20)
  expect(fn(5, 7, 8)).toBeTruthy()
})

test('skip evaluation of the second expression', () => {
  let effect = 'not evaluated'
  const F = function(){ return false }
  const Z = function(){ effect = 'Z got evaluated' }
  both(F, Z)()

  expect(effect).toBe('not evaluated')
})
R.both source
export function both(f, g){
  if (arguments.length === 1) return _g => both(f, _g)

  return (...input) => f(...input) && g(...input)
}

Try in REPL


clone

clone(objOrArr: T|T[]): T|T[] both Creates a deep copy of the value which may contain (nested) Arrays and Objects, Numbers, Strings, Booleans and Dates. Functions are assigned by reference rather than copied

const objects = [{}, {}, {}];
const objectsClone = R.clone(objects);
objects === objectsClone; //=> false
objects[0] === objectsClone[0]; //=> false
R.clone tests
import { clone } from './clone'
import assert from 'assert'

test('with array', () => {
  const arr = [
    {
      b : 2,
      c : 'foo',
      d : [ 1, 2, 3 ],
    },
    1,
    new Date(),
    null,
  ]
  expect(clone(arr)).toEqual(arr)
})

test('with object', () => {
  const arr = {
    a : 1,
    b : 2,
    c : 3,
    d : [ 1, 2, 3 ],
    e : new Date(),
  }
  expect(clone(arr)).toEqual(arr)
})

test('with date', () => {
  const date = new Date(2014, 10, 14, 23, 59, 59, 999)

  const cloned = clone(date)
  assert.notStrictEqual(date, cloned)
  expect(cloned).toEqual(new Date(2014, 10, 14, 23, 59, 59, 999))

  expect(cloned.getDay()).toEqual(5)
})
R.clone source
export function clone(val){
  const out = Array.isArray(val) ? Array(val.length) : {}
  if (val && val.getTime) return new Date(val.getTime())

  for (const key in val){
    const v = val[ key ]
    out[ key ] =
      typeof v === 'object' && v !== null ?
        v.getTime ?
          new Date(v.getTime()) :
          clone(v) :
        v
  }

  return out
}

Try in REPL


compose

compose(fn1: Function, ... , fnN: Function): any

It performs right-to-left function composition.

const result = R.compose(
  R.map(x => x * 2),both
  R.filter(x => x > 2)
)([1, 2, 3, 4])

// => [6, 8]
R.compose tests
import { add } from './add'
import { map } from './map'
import { multiply } from './multiply'
import { filter } from './filter'
import { last } from './last'
import { compose } from './compose'

test('happy', () => {
  const result = compose(
    last,
    map(add(10)),
    map(add(1))
  )([ 1, 2, 3 ])

  expect(result).toEqual(14)
})

test('accepts initially two arguments', () => {
  const result = compose(
    map(x => x * 2),
    (a, y) => filter(x => x > y, a)
  )([ 1, 2, 3, 4 ], 2)

  expect(result).toEqual([ 6, 8 ])
})

test('when no arguments is passed', () => {
  expect(() => compose()).toThrow('compose requires at least one argument')
})

test('ramda spec', () => {
  const f = function(a, b, c){ return [ a, b, c ] }
  const g = compose(f)
  expect(g(1, 2, 3)).toEqual([ 1, 2, 3 ])
})
R.compose source
export function compose(...fns){
  if (fns.length === 0){
    throw new Error('compose requires at least one argument')
  }

  return (...args) => {
    const list = fns.slice()
    if (list.length > 0){
      const fn = list.pop()
      let result = fn(...args)
      while (list.length > 0){
        result = list.pop()(result)
      }

      return result
    }
  }
}

Try in REPL


complement

complement(fn: Function): Function

It returns complemented function that accept input as argument.

The return value of complemented is the negative boolean value of fn(input).

const fn = R.complement(x => !x)

const result = fn(false) // => false
R.complement tests
import { complement } from './complement'

test('', () => {
  const fn = complement(x => x.length === 0)

  expect(fn([ 1, 2, 3 ])).toBeTruthy()
})

test('with multiple parameters', () => {
  const between = function(a, b, c){ return a < b && b < c }
  const f = complement(between)
  expect(f(4, 5, 11)).toEqual(false)
  expect(f(12, 2, 6)).toEqual(true)
})
R.complement source
export function complement(fn){
  return (...input) => !fn(...input)
}

Try in REPL


concat

concat(x: T[]|string, y: T[]|string): T[]|string

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'
R.concat tests
import { concat } from './concat'

test('', () => {
  const arr1 = [ 'a', 'b', 'c' ]
  const arr2 = [ 'd', 'e', 'f' ]

  const a = concat(arr1, arr2)
  const b = concat(arr1)(arr2)
  const expectedResult = [ 'a', 'b', 'c', 'd', 'e', 'f' ]

  expect(a).toEqual(expectedResult)
  expect(b).toEqual(expectedResult)
})

test('with strings', () => {
  expect(concat('ABC', 'DEF')).toEqual('ABCDEF')
})
R.concat source
export function concat(left, right){
  if (arguments.length === 1) return _right => concat(left, _right)

  return typeof left === 'string' ? `${ left }${ right }` : [ ...left, ...right ]
}

Try in REPL


curry

curry(fn: Function): Function

It returns curried version of fn.

const addFourNumbers = (a, b, c, d) => a + b + c + d
const curriedAddFourNumbers = R.curry(addFourNumbers)
const f = curriedAddFourNumbers(1, 2)
const g = f(3)
const result = g(4) // => 10
R.curry tests
import { curry } from './curry'

test('', () => {
  const addFourNumbers = (a, b, c, d) => a + b + c + d
  const curriedAddFourNumbers = curry(addFourNumbers)
  const f = curriedAddFourNumbers(1, 2)
  const g = f(3)

  expect(g(4)).toEqual(10)
})

test('when called with more arguments', () => {
  const add = curry((n, n2) => n + n2)

  expect(add(1, 2, 3)).toEqual(3)
})

test('when called with zero arguments', () => {
  const sub = curry((a, b) => a - b)
  const s0 = sub()

  expect(s0(5, 2)).toEqual(3)
})

test('when called via multiple curry stages', () => {
  const join = curry((a, b, c, d) => [ a, b, c, d ].join('-'))

  const stage1 = join('A')
  const stage2 = stage1('B', 'C')

  expect(stage2('D')).toEqual('A-B-C-D')
})
R.curry source
export function curry(fn, args = []){
  return (..._args) =>
    (rest => rest.length >= fn.length ? fn(...rest) : curry(fn, rest))([
      ...args,
      ..._args,
    ])
}

Try in REPL


dec

dec(x: number): number

It decrements a number.

R.dec(2) // => 1

Source

Try in REPL


defaultTo

defaultTo(defaultValue: T, ...inputArguments: any[]): T

It either returns defaultValue, if all of inputArguments are undefined, null or NaN.

Or it returns the first truthy inputArguments instance(from left to right).

R.defaultTo('foo', undefined) // => 'foo'
R.defaultTo('foo', undefined, null, NaN) // => 'foo'
R.defaultTo('foo', undefined, 'bar', NaN, 'baz') // => 'bar'
R.defaultTo('foo', undefined, null, NaN, 'baz') // => 'baz'
R.defaultTo('foo', 'bar') // => 'bar'
R.defaultTo tests
import { defaultTo } from './defaultTo'

test('with undefined', () => {
  expect(defaultTo('foo')(undefined)).toEqual('foo')
})

test('with null', () => {
  expect(defaultTo('foo')(null)).toEqual('foo')
})

test('with NaN', () => {
  expect(defaultTo('foo')(NaN)).toEqual('foo')
})

test('with empty string', () => {
  expect(defaultTo('foo', '')).toEqual('')
})

test('with false', () => {
  expect(defaultTo('foo', false)).toEqual(false)
})

test('when inputArgument passes initial check', () => {
  expect(defaultTo('foo', 'bar')).toEqual('bar')
})

test('default extends to indefinite input arguments - case 1', () => {
  const result = defaultTo('foo', null, 'bar')
  const expected = 'bar'

  expect(result).toEqual(expected)
})

test('default extends to indefinite input arguments - case 2', () => {
  const result = defaultTo('foo', null, NaN, 'bar')
  const expected = 'bar'

  expect(result).toEqual(expected)
})

test('default extends to indefinite input arguments - case 3', () => {
  const result = defaultTo('foo', null, NaN, undefined)
  const expected = 'foo'

  expect(result).toEqual(expected)
})

test('default extends to indefinite input arguments - case 4', () => {
  const result = defaultTo('foo', null, NaN, undefined, 'bar')
  const expected = 'bar'

  expect(result).toEqual(expected)
})

test('default extends to indefinite input arguments - case 5', () => {
  const result = defaultTo('foo', null, NaN, 'bar', 'baz')
  const expected = 'bar'

  expect(result).toEqual(expected)
})

test('default extends to indefinite input arguments - case 6', () => {
  const result = defaultTo('foo', null, NaN, undefined, null, NaN)
  const expected = 'foo'

  expect(result).toEqual(expected)
})
R.defaultTo source
function flagIs(inputArguments){
  return inputArguments === undefined ||
    inputArguments === null ||
    Number.isNaN(inputArguments) === true
}

export function defaultTo(defaultArgument, ...inputArguments){
  if (arguments.length === 1){
    return _inputArguments => defaultTo(defaultArgument, _inputArguments)
  } else if (arguments.length === 2){
    return flagIs(inputArguments[ 0 ]) ? defaultArgument : inputArguments[ 0 ]
  }

  const limit = inputArguments.length - 1
  let len = limit + 1
  let ready = false
  let holder

  while (!ready){
    const instance = inputArguments[ limit - len + 1 ]

    if (len === 0){
      ready = true
    } else if (flagIs(instance)){
      len -= 1
    } else {
      holder = instance
      ready = true
    }
  }

  return holder === undefined ?
    defaultArgument :
    holder
}

Try in REPL


dissoc

dissoc(prop: any, obj: object): object

It returns a new object that does not contain a prop property.

R.dissoc('b', {a: 1, b: 2, c: 3})
//=> {a: 1, c: 3}
R.dissoc tests
import { dissoc } from './dissoc'

test('input is null or undefined', () => {
  //These tests match Ramda behavior
  //https://ramdajs.com/repl/?v=0.25.0#?R.dissoc%28%27b%27%2C%20null%29
  expect(dissoc('b', null)).toEqual({})
  //https://ramdajs.com/repl/?v=0.25.0#?R.dissoc%28%27b%27%2C%20undefined%29
  expect(dissoc('b', undefined)).toEqual({})
})

test('property exists curried', () => {
  expect(
    dissoc('b')({
      a : 1,
      b : 2,
    })
  ).toEqual({ a : 1 })
})

test('property doesn\'t exists', () => {
  expect(
    dissoc('c', {
      a : 1,
      b : 2,
    })
  ).toEqual({
    a : 1,
    b : 2,
  })
})

test('works with non-string property', () => {
  expect(
    dissoc(42, {
      a  : 1,
      42 : 2,
    })
  ).toEqual({ a : 1 })

  expect(
    dissoc(null, {
      a    : 1,
      null : 2,
    })
  ).toEqual({ a : 1 })

  expect(
    dissoc(undefined, {
      a         : 1,
      undefined : 2,
    })
  ).toEqual({ a : 1 })
})

test('includes prototype properties', () => {
  function Rectangle(width, height){
    this.width = width
    this.height = height
  }
  const area = Rectangle.prototype.area = function(){
    return this.width * this.height
  }
  const rect = new Rectangle(7, 6)

  expect(dissoc('area', rect)).toEqual({
    width  : 7,
    height : 6,
  })

  expect(dissoc('width', rect)).toEqual({
    height : 6,
    area   : area,
  })

  expect(dissoc('depth', rect)).toEqual({
    width  : 7,
    height : 6,
    area   : area,
  })
})
R.dissoc source
export function dissoc(prop, obj){
  if (arguments.length === 1) return _obj => dissoc(prop, _obj)

  if (obj === null || obj === undefined) return {}

  const willReturn = {}
  for (const p in obj){
    willReturn[ p ] = obj[ p ]
  }
  delete willReturn[ prop ]

  return willReturn
}

Try in REPL


divide

R.divide(71, 100) // => 0.71

drop

drop(howManyToDrop: number, arrOrStr: T[]|string): T[]|String

It returns arrOrStr with howManyToDrop items dropped from the left.

R.drop(1, ['foo', 'bar', 'baz']) // => ['bar', 'baz']
R.drop(1, 'foo')  // => 'oo'
R.drop tests
import { drop } from './drop'
import assert from 'assert'

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 string', () => {
  expect(drop(3, 'rambda')).toEqual('bda')
})

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(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})

test('should return copy', () => {
  const xs = [ 1, 2, 3 ]

  assert.notStrictEqual(drop(0, xs), xs)
  assert.notStrictEqual(drop(-1, xs), xs)
})
R.drop source
export function drop(n, listOrString){
  if (arguments.length === 1) return _list => drop(n, _list)

  return listOrString.slice(n > 0 ? n : 0)
}

Try in REPL


dropLast

dropLast(howManyToDrop: number, arrOrStr: T[]|String): T[]|String

It returns arrOrStr with howManyToDrop items dropped from the right.

R.dropLast(1, ['foo', 'bar', 'baz']) // => ['foo', 'bar']
R.dropLast(1, 'foo')  // => 'fo'
R.dropLast tests
import { dropLast } from './dropLast'
import assert from 'assert'

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 string', () => {
  expect(dropLast(3, 'rambda')).toEqual('ram')
})

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(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})

test('should return copy', () => {
  const xs = [ 1, 2, 3 ]

  assert.notStrictEqual(dropLast(0, xs), xs)
  assert.notStrictEqual(dropLast(-1, xs), xs)
})
R.dropLast source
export function dropLast(n, list){
  if (arguments.length === 1) return _list => dropLast(n, _list)

  return n > 0 ? list.slice(0, -n) : list.slice()
}

Try in REPL


endsWith

endsWith(x: string, str: string): boolean

R.endsWith(
  'bar',
  'foo-bar'
) // => true

R.endsWith(
  'foo',
  'foo-bar'
) // => false
R.endsWith tests
import { endsWith } from './endsWith'

test('string ends with suffix', () => {
  expect(endsWith('bar', 'foo-bar')).toBeTruthy()
})

test('currying', () => {
  expect(endsWith('baz')('foo-bar')).toBeFalsy()
})

test('list ends with suffix', () => {
  expect(() => endsWith([ 'c' ], [ 'a', 'b', 'c' ])).toThrow('list.endsWith is not a function')
})
R.endsWith source
export function endsWith(suffix, list){
  if (arguments.length === 1) return _list => endsWith(suffix, _list)

  return list.endsWith(suffix)
}

Try in REPL


either

either(firstCondition: Function, secondCondition: Function): Function

R.either(
  a => a > 10,
  a => a % 2 === 0
)(15) //=> true
R.either tests
import { either } from './either'

test('with multiple inputs', () => {
  const between = function(a, b, c){ return a < b && b < c }
  const total20 = function(a, b, c){ return a + b + c === 20 }
  const fn = either(between, total20)
  expect(fn(7, 8, 5)).toBeTruthy()
})

test('skip evaluation of the second expression', () => {
  let effect = 'not evaluated'
  const F = function(){ return true }
  const Z = function(){ effect = 'Z got evaluated' }
  either(F, Z)()

  expect(effect).toBe('not evaluated')
})

test('case 1', () => {
  const firstFn = val => val > 0
  const secondFn = val => val * 5 > 10

  expect(either(firstFn, secondFn)(1)).toBeTruthy()
})

test('case 2', () => {
  const firstFn = val => val > 0
  const secondFn = val => val === -10
  const fn = either(firstFn)(secondFn)

  expect(fn(-10)).toBeTruthy()
})
R.either source
export function either(f, g){
  if (arguments.length === 1) return _g => either(f, _g)

  return (...input) => f(...input) || g(...input)
}

Try in REPL


equals

equals(a: any, b: any): boolean

It returns equality match between a and b.

It doesn't handle cyclical data structures.

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

test('happy', () => {
  const result = equals(
    [ 1, { a : 1 }, [ { b : 3 } ] ],
    [ 1, { a : 2 }, [ { b : 3 } ] ]
  )

  expect(result).toBeFalsy()
})

test('with regex', () => {
  expect(equals(/s/, /s/)).toEqual(true)
  expect(equals(/s/, /d/)).toEqual(false)
  expect(equals(/a/gi, /a/ig)).toEqual(true)
  expect(equals(/a/mgi, /a/img)).toEqual(true)
  expect(equals(/a/gi, /a/i)).toEqual(false)
})

test('not a number', () => {
  expect(equals([ NaN ], [ NaN ])).toBe(true)
})

test('new number', () => {
  expect(equals(new Number(0), new Number(0))).toEqual(true)
  expect(equals(new Number(0), new Number(1))).toEqual(false)
  expect(equals(new Number(1), new Number(0))).toEqual(false)
})

test('new string', () => {
  expect(equals(new String(''), new String(''))).toEqual(true)
  expect(equals(new String(''), new String('x'))).toEqual(false)
  expect(equals(new String('x'), new String(''))).toEqual(false)
  expect(equals(new String('foo'), new String('foo'))).toEqual(true)
  expect(equals(new String('foo'), new String('bar'))).toEqual(false)
  expect(equals(new String('bar'), new String('foo'))).toEqual(false)
})

test('new Boolean', () => {
  expect(equals(new Boolean(true), new Boolean(true))).toEqual(true)
  expect(equals(new Boolean(false), new Boolean(false))).toEqual(true)
  expect(equals(new Boolean(true), new Boolean(false))).toEqual(false)
  expect(equals(new Boolean(false), new Boolean(true))).toEqual(false)
})

test('new Error', () => {
  expect(equals(new Error('XXX'), {})).toEqual(false)
  expect(equals(new Error('XXX'), new TypeError('XXX'))).toEqual(false)
  expect(equals(new Error('XXX'), new Error('YYY'))).toEqual(false)
  expect(equals(new Error('XXX'), new Error('XXX'))).toEqual(true)
  expect(equals(new Error('XXX'), new TypeError('YYY'))).toEqual(false)
})

test('with dates', () => {
  expect(equals(new Date(0), new Date(0))).toEqual(true)
  expect(equals(new Date(1), new Date(1))).toEqual(true)
  expect(equals(new Date(0), new Date(1))).toEqual(false)
  expect(equals(new Date(1), new Date(0))).toEqual(false)
  expect(equals(new Date(0), {})).toEqual(false)
  expect(equals({}, new Date(0))).toEqual(false)
})

test('ramda spec', () => {
  expect(equals({}, {})).toEqual(true)

  expect(
    equals(
      {
        a : 1,
        b : 2,
      },
      {
        a : 1,
        b : 2,
      }
    )
  ).toEqual(true)

  expect(
    equals(
      {
        a : 2,
        b : 3,
      },
      {
        b : 3,
        a : 2,
      }
    )
  ).toEqual(true)

  expect(
    equals(
      {
        a : 2,
        b : 3,
      },
      {
        a : 3,
        b : 3,
      }
    )
  ).toEqual(false)

  expect(
    equals(
      {
        a : 2,
        b : 3,
        c : 1,
      },
      {
        a : 2,
        b : 3,
      }
    )
  ).toEqual(false)
})

test('works with boolean tuple', () => {
  expect(equals([ true, false ], [ true, false ])).toBeTruthy()
  expect(equals([ true, false ], [ true, true ])).toBeFalsy()
})

test('works with equal objects within array', () => {
  const objFirst = {
    a : {
      b : 1,
      c : 2,
      d : [ 1 ],
    },
  }
  const objSecond = {
    a : {
      b : 1,
      c : 2,
      d : [ 1 ],
    },
  }

  const x = [ 1, 2, objFirst, null, '', [] ]
  const y = [ 1, 2, objSecond, null, '', [] ]
  expect(equals(x, y)).toBeTruthy()
})

test('works with different objects within array', () => {
  const objFirst = { a : { b : 1 } }
  const objSecond = { a : { b : 2 } }

  const x = [ 1, 2, objFirst, null, '', [] ]
  const y = [ 1, 2, objSecond, null, '', [] ]
  expect(equals(x, y)).toBeFalsy()
})

test('works with undefined as second argument', () => {
  expect(equals(1, undefined)).toBeFalsy()

  expect(equals(undefined, undefined)).toBeTruthy()
})

test('various examples', () => {
  expect(equals([ 1, 2, 3 ])([ 1, 2, 3 ])).toBeTruthy()

  expect(equals([ 1, 2, 3 ], [ 1, 2 ])).toBeFalsy()

  expect(equals(1, 1)).toBeTruthy()

  expect(equals(1, '1')).toBeFalsy()

  expect(equals({}, {})).toBeTruthy()

  expect(
    equals(
      {
        a : 1,
        b : 2,
      },
      {
        b : 2,
        a : 1,
      }
    )
  ).toBeTruthy()

  expect(
    equals(
      {
        a : 1,
        b : 2,
      },
      {
        a : 1,
        b : 1,
      }
    )
  ).toBeFalsy()

  expect(
    equals(
      {
        a : 1,
        b : false,
      },
      {
        a : 1,
        b : 1,
      }
    )
  ).toBeFalsy()

  expect(
    equals(
      {
        a : 1,
        b : 2,
      },
      {
        b : 2,
        a : 1,
        c : 3,
      }
    )
  ).toBeFalsy()

  expect(
    equals(
      {
        x : {
          a : 1,
          b : 2,
        },
      },
      {
        x : {
          b : 2,
          a : 1,
          c : 3,
        },
      }
    )
  ).toBeFalsy()

  expect(
    equals(
      {
        a : 1,
        b : 2,
      },
      {
        b : 3,
        a : 1,
      }
    )
  ).toBeFalsy()

  expect(
    equals({ a : { b : { c : 1 } } }, { a : { b : { c : 1 } } })
  ).toBeTruthy()

  expect(
    equals({ a : { b : { c : 1 } } }, { a : { b : { c : 2 } } })
  ).toBeFalsy()

  expect(equals({ a : {} }, { a : {} })).toBeTruthy()

  expect(equals('', '')).toBeTruthy()

  expect(equals('foo', 'foo')).toBeTruthy()

  expect(equals('foo', 'bar')).toBeFalsy()

  expect(equals(0, false)).toBeFalsy()

  expect(equals(/\s/g, null)).toBeFalsy()

  expect(equals(null, null)).toBeTruthy()

  expect(equals(false)(null)).toBeFalsy()
})
R.equals source
import { type } from './type'

function parseError(maybeError){
  const typeofError = maybeError.__proto__.toString()
  if (![ 'Error', 'TypeError' ].includes(typeofError)) return []

  return [ typeofError, maybeError.message ]
}

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 equals(a, b){
  if (arguments.length === 1) return _b => equals(a, _b)

  const aType = type(a)

  if (aType !== type(b)) return false
  if ([ 'NaN', 'Undefined', 'Null' ].includes(aType)) return true
  if ([ 'Boolean', 'Number', '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 ] &&
          !equals(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
  } else if (bRegex[ 0 ]) return false

  const aDate = parseDate(a)
  const bDate = parseDate(b)

  if (aDate[ 0 ]){
    return bDate[ 0 ] ? aDate[ 1 ] === bDate[ 1 ] : false
  } else if (bDate[ 0 ]) return false

  const aError = parseError(a)
  const bError = parseError(b)

  if (
    aError[ 0 ]
  ){
    return bError[ 0 ] ?
      aError[ 0 ] === bError[ 0 ] && aError[ 1 ] === bError[ 1 ] :
      false
  }

  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 && !equals(aValue, bValue)){
          loopObjectFlag = false
        }
      }
    })

    return loopObjectFlag
  }

  return false
}

Try in REPL


F

R.F() // => false

Source


filter

filter(filterFn: Function, x: Array|Object): Array|Object

It filters x iterable over boolean returning filterFn.

const filterFn = a => a % 2 === 0

const result = R.filter(filterFn, [1, 2, 3, 4])
// => [2, 4]
R.filter tests
import { filter } from './filter'
import { compose } from './compose'
import { add } from './add'
import { map } from './map'
import { equals } from './equals'
import { T } from './T'

const sampleObject = {
  a : 1,
  b : 2,
  c : 3,
  d : 4,
}

test('with compose', () => {
  const result = compose(
    filter(equals(2)),
    map(add(1))
  )(sampleObject)

  expect(result).toEqual({ a : 2 })
})

test('bad case - undefined', () => {
  expect(filter(T)(undefined)).toEqual([])
})

test('with object it passes property as second argument', () => {
  filter((val, prop) => {
    expect(typeof prop).toEqual('string')
  })(sampleObject)
})

test('pass input object as third argument', () => {
  const obj = {
    a : 1,
    b : 2,
  }
  const predicate = (val, prop, inputObject) => {
    expect(inputObject).toEqual(obj)

    return val < 2
  }
  expect(filter(predicate, obj)).toEqual({ a : 1 })
})

test('with array', () => {
  const isEven = n => n % 2 === 0

  expect(filter(isEven, [ 1, 2, 3, 4 ])).toEqual([ 2, 4 ])
})

test('pass index as second argument', () => {
  let counter = 0
  filter(
    (x, i) => {
      expect(i).toBe(counter)
      counter++
    },
    [ 10, 20, 30 ]
  )
})

test('with object', () => {
  const isEven = n => n % 2 === 0
  const result = filter(isEven, sampleObject)
  const expectedResult = {
    b : 2,
    d : 4,
  }

  expect(result).toEqual(expectedResult)
})
R.filter source
function filterObject(fn, obj){
  const willReturn = {}

  for (const prop in obj){
    if (fn(obj[ prop ], prop, obj)){
      willReturn[ prop ] = obj[ prop ]
    }
  }

  return willReturn
}

export function filter(fn, list){
  if (arguments.length === 1) return _list => filter(fn, _list)

  if (list === undefined){
    return []
  }

  if (!Array.isArray(list)){
    return filterObject(fn, list)
  }

  let index = -1
  let resIndex = 0
  const len = list.length
  const willReturn = []

  while (++index < len){
    const value = list[ index ]

    if (fn(value, index)){
      willReturn[ resIndex++ ] = value
    }
  }

  return willReturn
}

The method works with objects as well.

Note that unlike Ramda's filter, here object keys are passed as second argument to filterFn.

const result = R.filter((val, prop)=>{
  return prop === 'a' || val === 2
}, {a: 1, b: 2, c: 3})

// => {a: 1, b: 2}

Try in REPL


find

find(findFn: Function, arr: T[]): T|undefined

It returns undefined or the first element of arr satisfying findFn.

const findFn = a => R.type(a.foo) === 'Number'
const arr = [{foo: 'bar'}, {foo: 1}]

const result = R.find(findFn, arr)
// => {foo: 1}
R.find tests
import { find } from './find'
import { propEq } from './propEq'

test('', () => {
  expect(
    find(propEq('a', 2), [ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual({ a : 2 })
})

test('with curry', () => {
  expect(
    find(propEq('a', 4))([ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual(undefined)
})
R.find source
export function find(fn, list){
  if (arguments.length === 1) return _list => find(fn, _list)

  return list.find(fn)
}

Try in REPL


findIndex

findIndex(findFn: Function, arr: T[]): number

It returns -1 or the index of the first element of arr satisfying findFn.

const findFn = a => R.type(a.foo) === 'Number'
const arr = [{foo: 'bar'}, {foo: 1}]

const result = R.findIndex(findFn, arr)
// => 1
R.findIndex tests
import { findIndex } from './findIndex'
import { propEq } from './propEq'

test('', () => {
  expect(
    findIndex(propEq('a', 2))([ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual(1)

  expect(
    findIndex(propEq('a', 1))([ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual(0)

  expect(
    findIndex(propEq('a', 4))([ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual(-1)
})

test('pass index as second argument', () => {
  findIndex(
    (x, i) => {
      expect(typeof x).toBe('number')
      expect(typeof i).toBe('number')
    }
  )([ 10, 12, 15 ])
})
R.findIndex source
export function findIndex(fn, list){
  if (arguments.length === 1) return _list => findIndex(fn, _list)

  const len = list.length
  let index = -1

  while (++index < len){
    if (fn(list[ index ], index)){
      return index
    }
  }

  return -1
}

Try in REPL


findLast

findLast(findFn: Function, arr: T[]): T|undefined

It returns undefined or the last element of arr satisfying findFn.

const findFn = a => R.type(a.foo) === 'Number'
const arr = [{foo: 'bar'}, {foo: 1}]

const result = R.findLast(findFn, arr)
// => {foo: 1}
R.findLast tests
import { findLast } from './findLast'

test('happy', () => {
  const result = findLast((x, i) => {
    expect(typeof i).toBe('number')
    return x > 1
  }, [ 1, 1, 1, 2, 3, 4, 1 ])
  expect(
    result
  ).toEqual(4)

  expect(
    findLast(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])
  ).toEqual(0)
})

test('with curry', () => {
  expect(
    findLast(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])
  ).toEqual(4)
})

const obj1 = { x : 100 }
const obj2 = { x : 200 }
const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ]
const even = function(x){ return x % 2 === 0 }
const gt100 = function(x){ return x > 100 }
const isStr = function(x){ return typeof x === 'string' }
const xGt100 = function(o){ return o && o.x > 100 }

test('ramda 1', () => {
  expect(findLast(even, a)).toEqual(0)
  expect(findLast(gt100, a)).toEqual(300)
  expect(findLast(isStr, a)).toEqual('cow')
  expect(findLast(xGt100, a)).toEqual(obj2)
})

test('ramda 2', () => {
  expect(findLast(even, [ 'zing' ])).toEqual(undefined)
})

test('ramda 3', () => {
  expect(findLast(even, [ 2, 3, 5 ])).toEqual(2)
})

test('ramda 4', () => {
  expect(findLast(even, [])).toEqual(undefined)
})
R.findLast source
export function findLast(fn, list){
  if (arguments.length === 1) return _list => findLast(fn, _list)

  let index = list.length

  while (--index >= 0){
    if (fn(list[ index ], index)){
      return list[ index ]
    }
  }

  return undefined
}

Try in REPL


findLastIndex

findLastIndex(findFn: Function, arr: T[]): number

It returns -1 or the last index of the first element of arr satisfying findFn.

const findFn = a => R.type(a.foo) === 'Number'
const arr = [{foo: 'bar'}, {foo: 1}]

const result = R.findLastIndex(findFn, arr)
// => 1
R.findLastIndex tests
import { findLastIndex } from './findLastIndex'

test('happy', () => {
  const result = findLastIndex((x, i) => {
    expect(typeof i).toBe('number')
    return x > 1
  }, [ 1, 1, 1, 2, 3, 4, 1 ])
  
  expect(
    result
  ).toEqual(5)

  expect(findLastIndex(x => x === 0, [ 0, 1, 1, 2, 3, 4, 1 ])).toEqual(0)
})

test('with curry', () => {
  expect(findLastIndex(x => x > 1)([ 1, 1, 1, 2, 3, 4, 1 ])).toEqual(5)
})

const obj1 = { x : 100 }
const obj2 = { x : 200 }
const a = [ 11, 10, 9, 'cow', obj1, 8, 7, 100, 200, 300, obj2, 4, 3, 2, 1, 0 ]
const even = function(x){
  return x % 2 === 0
}
const gt100 = function(x){
  return x > 100
}
const isStr = function(x){
  return typeof x === 'string'
}
const xGt100 = function(o){
  return o && o.x > 100
}

test('ramda 1', () => {
  expect(findLastIndex(even, a)).toEqual(15)
  expect(findLastIndex(gt100, a)).toEqual(9)
  expect(findLastIndex(isStr, a)).toEqual(3)
  expect(findLastIndex(xGt100, a)).toEqual(10)
})

test('ramda 2', () => {
  expect(findLastIndex(even, [ 'zing' ])).toEqual(-1)
})

test('ramda 3', () => {
  expect(findLastIndex(even, [ 2, 3, 5 ])).toEqual(0)
})

test('ramda 4', () => {
  expect(findLastIndex(even, [])).toEqual(-1)
})
R.findLastIndex source
export function findLastIndex(fn, list){
  if (arguments.length === 1) return _list => findLastIndex(fn, _list)

  let index = list.length

  while (--index >= 0){
    if (fn(list[ index ], index)){
      return index
    }
  }

  return -1
}

Try in REPL


flatten

flatten(arr: any[]): any[]

R.flatten([ 1, [ 2, [ 3 ] ] ])
// => [ 1, 2, 3 ]
R.flatten tests
import { flatten } from './flatten'

test('', () => {
  expect(flatten([ 1, 2, 3, [ [ [ [ [ 4 ] ] ] ] ] ])).toEqual([ 1, 2, 3, 4 ])

  expect(flatten([ 1, [ 2, [ [ 3 ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ])

  expect(flatten([ 1, [ 2, [ [ [ 3 ] ] ] ], [ 4 ] ])).toEqual([ 1, 2, 3, 4 ])

  expect(
    flatten([ 1, 2, [ 3, 4 ], 5, [ 6, [ 7, 8, [ 9, [ 10, 11 ], 12 ] ] ] ])
  ).toEqual([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ])
})
R.flatten source
export function flatten(list, input){
  const willReturn = input === undefined ? [] : input

  for (let i = 0; i < list.length; i++){
    if (Array.isArray(list[ i ])){
      flatten(list[ i ], willReturn)
    } else {
      willReturn.push(list[ i ])
    }
  }

  return willReturn
}

Try in REPL


flip

flip(fn: Function): Function

It returns function which calls fn with exchanged first and second argument.

const subtractFlip = R.flip(R.subtract)

const result = subtractFlip(1,7)
// => 6
R.flip tests
import { flip } from './flip'
import { subtract } from './subtract'

test('flip', () => {
  const fn = flip(subtract)

  expect(fn(1)(7)).toEqual(6)
  expect(fn(1, 7)).toEqual(6)
  expect(fn(1, 7, 9)).toEqual(undefined)
})
R.flip source
function flipExport(fn){
  return (...input) => {
    if (input.length === 1){
      return holder => fn(holder, input[ 0 ])
    } else if (input.length === 2){
      return fn(input[ 1 ], input[ 0 ])
    }

    return undefined
  }
}

export function flip(fn){
  return flipExport(fn)
}

Try in REPL


forEach

forEach(fn: Function, arr: Array): Array

It applies function fn over all members of array arr and returns arr.

const sideEffect = {}
const result = R.forEach(
  x => sideEffect[`foo${x}`] = x
)([1, 2])

console.log(sideEffect) //=> {foo1 : 1, foo2 : 2}
console.log(result) //=> [1, 2]
R.forEach tests
import { forEach } from './forEach'
import { type } from './type'

test('iterate over object', () => {
  const obj = {
    a : 1,
    b : [ 1, 2 ],
    c : { d : 7 },
    f : 'foo',
  }
  const result = {}
  const getLength = x => Object.keys(x).length
  forEach(
    (val, prop, inputObj) =>
      result[ prop ] = `${ prop }::${ type(val) }::${ getLength(inputObj) }`
  )(obj)

  const expected = {
    a : 'a::Number::4',
    b : 'b::Array::4',
    c : 'c::Object::4',
    f : 'f::String::4',
  }

  expect(result).toEqual(expected)
})

test('happy', () => {
  const sideEffect = {}
  forEach(x => sideEffect[ `foo${ x }` ] = x + 10)([ 1, 2 ])

  expect(sideEffect).toEqual({
    foo1 : 11,
    foo2 : 12,
  })
})

test('happy 2', () => {
  const list = [
    {
      x : 1,
      y : 2,
    },
    {
      x : 100,
      y : 200,
    },
    {
      x : 300,
      y : 400,
    },
    {
      x : 234,
      y : 345,
    },
  ]
  const sideEffect = {}
  const result = forEach(elem => {
    sideEffect[ elem.x ] = elem.y
  }, list)
  const expectedSideEffect = {
    1   : 2,
    100 : 200,
    300 : 400,
    234 : 345,
  }

  expect(sideEffect).toEqual(expectedSideEffect)
  expect(result).toEqual(list)
})

test('with empty list', () => {
  const list = []
  const result = forEach(x => x * x)(list)

  expect(result).toEqual(list)
})

test('returns the input', () => {
  const list = [ 1, 2, 3 ]
  const result = forEach(x => x * x)(list)

  expect(result).toEqual(list)
})

test('pass index as second argument', () => {
  const list = [ 11, 21, 31 ]
  const indexes = []
  const result = forEach((x, i) => indexes.push(i))(list)

  expect(indexes).toEqual([ 0, 1, 2 ])
})
R.forEach source
import { map } from './map'

export function forEach(fn, list){
  if (arguments.length === 1) return _list => forEach(fn, _list)

  map(fn, list)

  return list
}

Note, that unlike Ramda's forEach, Rambda's one doesn't dispatch to forEach method of arr if arr has such method.

Try in REPL


fromPairs

fromPairs(list: any[]): object

It transforms a list to an object.

const list = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ]
const expected = {
  a : 1,
  b : 2,
  c : [ 3, 4 ],
}

const result = R.fromPairs(list)
// expected === result
R.fromPairs tests
import { fromPairs } from './fromPairs'

const list = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ]
const expected = {
  a : 1,
  b : 2,
  c : [ 3, 4 ],
}

test('', () => {
  expect(fromPairs(list)).toEqual(expected)
})
R.fromPairs source
export function fromPairs(list){
  const toReturn = {}
  list.forEach(([ prop, value ]) => toReturn[ prop ] = value)

  return toReturn
}

Try in REPL


groupBy

groupBy(fn: Function, arr: Array): Object

It groups array arr by provided selector function fn.

R.groupBy(
  x => x.length,
  [ 'a', 'b', 'aa', 'bb' ]
)
// => { '1': ['a', 'b'], '2': ['aa', 'bb'] }

groupWith

groupWith(fn: Function, arr: Array): Object

It creates a groups of array members defined by equality function fn.

const list = [ 4, 3, 6, 2, 2, 1 ]
const result = R.groupWith(
  (a,b) => a - b === 0,
  list
)
const expected = [
  [ 4, 3 ],
  [ 6 ],
  [ 2 ],
  [ 2, 1 ],
]
// result === expected

has

has(prop: string, obj: Object): boolean

  • It returns true if obj has property prop.
R.has('a', {a: 1}) // => true
R.has('b', {a: 1}) // => false
R.has tests
import { has } from './has'

test('happy', () => {
  expect(has('a')({ a : 1 })).toBeTruthy()
  expect(has('b', { a : 1 })).toBeFalsy()
})

test('with non-object', () => {
  expect(has('a', undefined)).toEqual(false)
  expect(has('a', null)).toEqual(false)
  expect(has('a', true)).toEqual(false)
  expect(has('a', '')).toEqual(false)
  expect(has('a', /a/)).toEqual(false)
})
R.has source
export function has(prop, obj){
  if (arguments.length === 1) return _obj => has(prop, _obj)

  if (!obj) return false

  return obj[ prop ] !== undefined
}

Try in REPL


head

head(arrOrStr: T[]|string): T|string

It returns the first element of arrOrStr.

R.head([1, 2, 3]) // => 1
R.head('foo') // => 'f'
R.head tests
import { head } from './head'

test('head', () => {
  expect(head([ 'fi', 'fo', 'fum' ])).toEqual('fi')
  expect(head([])).toEqual(undefined)
  expect(head('foo')).toEqual('f')
  expect(head('')).toEqual('')
})
R.head source
export function head(list){
  if (typeof list === 'string') return list[ 0 ] || ''

  return list[ 0 ]
}

Try in REPL


identical

identical(a: any, b: any): boolean

Returns true if its arguments are identical, false otherwise. Values are identical if they reference the same memory. NaN is identical to NaN; 0 and -0 are not identical.

const o = {};
R.identical(o, o); //=> true
R.identical(1, 1); //=> true
R.identical(1, '1'); //=> false
R.identical([], []); //=> false
R.identical(0, -0); //=> false
R.identical(NaN, NaN); //=> true
R.identical tests
import { identical } from './identical'
import { _isInteger } from './internal/_isInteger'
import { _objectIs } from './internal/_objectIs'
import { F } from './F'
import { T } from './T'

test('small', () => {
  expect(F()).toBe(false)
  expect(T()).toBe(true)
})

test('is integer internal', () => {
  expect(_isInteger(1)).toBe(true)
  expect(_isInteger(0.3)).toBe(false)
})

test('object is internal', () => {
  expect(_objectIs(1, 1)).toBe(true)
  expect(_objectIs(NaN, NaN)).toBe(true)
})

test('identical', () => {
  const a = {}

  expect(identical(100)(100)).toEqual(true)
  expect(identical(100, '100')).toEqual(false)
  expect(identical('string', 'string')).toEqual(true)
  expect(identical([], [])).toEqual(false)
  expect(identical(a, a)).toEqual(true)
  expect(identical(undefined, undefined)).toEqual(true)
  expect(identical(null, undefined)).toEqual(false)
})
R.identical source
import _objectIs from './internal/_objectIs'

export function identical(a, b){
  if (arguments.length === 1) return _b => identical(a, _b)

  return _objectIs(a, b)
}

Try in REPL


identity

identity(x: T): T

It just passes back the supplied arguments.

R.identity(7) // => 7
R.identity tests
import { identity } from './identity'

test('', () => {
  expect(identity(7)).toEqual(7)
})
R.identity source
export function identity(x){
  return x
}

Try in REPL


ifElse

ifElse(condition: Function|boolean, ifFn: Function, elseFn: Function): Function

It returns function, which expect input as argument and returns finalResult.

When this function is called, a value answer is generated as a result of condition(input).

If answer is true, then finalResult is equal to ifFn(input). If answer is false, then finalResult is equal to elseFn(input).

const fn = R.ifElse(
 x => x > 10,
 x => x*2,
 x => x*10
)

const result = fn(8)
// => 80
R.ifElse tests
import { has } from './has'
import { identity } from './identity.js'
import { prop } from './prop'
import { always } from './always'
import { ifElse } from './ifElse'

const condition = has('foo')
const v = function(a){ return typeof a === 'number' }
const t = function(a){ return a + 1 }
const ifFn = x => prop('foo', x).length
const elseFn = () => false

test('happy', () => {
  const fn = ifElse(condition, ifFn)(elseFn)

  expect(fn({ foo : 'bar' })).toEqual(3)
  expect(fn({ fo : 'bar' })).toEqual(false)
})

test('ramda spec', () => {
  const ifIsNumber = ifElse(v)
  expect(ifIsNumber(t, identity)(15)).toEqual(16)
  expect(ifIsNumber(t, identity)('hello')).toEqual('hello')
})

test('pass all arguments', () => {
  const identity = function(a){ return a }
  const v = function(){ return true }
  const onTrue = function(a, b){
    expect(a).toEqual(123)
    expect(b).toEqual('abc')
  }
  ifElse(v, onTrue, identity)(123, 'abc')
})

test('accept constant as condition', () => {
  const fn = ifElse(true)(always(true))(always(false))

  expect(fn()).toEqual(true)
})

test('accept constant as condition - case 2', () => {
  const fn = ifElse(false, always(true), always(false))

  expect(fn()).toEqual(false)
})

test('curry 1', () => {
  const fn = ifElse(condition, ifFn)(elseFn)

  expect(fn({ foo : 'bar' })).toEqual(3)
  expect(fn({ fo : 'bar' })).toEqual(false)
})

test('curry 2', () => {
  const fn = ifElse(condition)(ifFn)(elseFn)

  expect(fn({ foo : 'bar' })).toEqual(3)
  expect(fn({ fo : 'bar' })).toEqual(false)
})
R.ifElse source
import { curry } from './curry'

export const ifElse = curry(ifElseFn)

Try in REPL


inc

inc(x: number): number

It increments a number.

R.inc(1) // => 2

Source

Try in REPL


includes

includes(valueToFind: T|string, input: T[]|string): boolean

If input is string, then this method work as native includes. If input is array, then R.equals is used to define if valueToFind belongs to the list.

R.includes('oo', 'foo') // => true
R.includes({a: 1}, [{a: 1}]) // => true
R.includes tests
import { includes } from './includes'
import R from 'ramda'

test('includes with string', () => {
  const str = 'more is less'

  expect(includes('less')(str)).toBeTruthy()
  expect(R.includes('less')(str)).toBeTruthy()
  expect(includes('never', str)).toBeFalsy()
  expect(R.includes('never', str)).toBeFalsy()
})

test('includes with array', () => {
  const arr = [ 1, 2, 3 ]

  expect(includes(2)(arr)).toBeTruthy()
  expect(R.includes(2)(arr)).toBeTruthy()

  expect(includes(4, arr)).toBeFalsy()
  expect(R.includes(4, arr)).toBeFalsy()
})

test('return false if input is falsy', () => {
  expect(includes(2, null)).toBeFalsy()
  expect(() => R.includes(2, null)).toThrow()
  expect(includes(4, undefined)).toBeFalsy()
  expect(() => R.includes(4, undefined)).toThrow()
})
R.includes source
import { equals } from './equals'

export function includes(target, list){
  if (arguments.length === 1) return _input => includes(target, _input)

  if (typeof list === 'string'){
    return list.includes(target)
  }
  if (!Array.isArray(list)) return false

  let index = -1

  while (++index < list.length){
    if (equals(list[ index ], target)){
      return true
    }
  }

  return false
}

Try in REPL


indexBy

indexBy(fn: Function, arr: T[]): Object

It indexes array arr as an object with provided selector function fn.

R.indexBy(
  x => x.id,
  [ {id: 1}, {id: 2} ]
)
// => { 1: {id: 1}, 2: {id: 2} }
R.indexBy tests
import { indexBy } from './indexBy'
import { prop } from './prop'

test('indexBy', () => {
  const list = [ { id : 1 }, { id : 2 }, { id : 10 }, { id : 'a' } ]

  expect(indexBy(prop('id'))(list)).toEqual({
    1  : { id : 1 },
    2  : { id : 2 },
    10 : { id : 10 },
    a  : { id : 'a' },
  })
})
R.indexBy source
export function indexBy(fn, list){
  if (arguments.length === 1) return _list => indexBy(fn, _list)

  const result = {}
  for (let i = 0; i < list.length; i++){
    const item = list[ i ]
    result[ fn(item) ] = item
  }

  return result
}

Try in REPL


indexOf

indexOf(valueToFind: any, arr: T[]): number

It returns -1 or the index of the first element of arr equal of valueToFind.

R.indexOf(1, [1, 2]) // => 0
R.indexOf(0, [1, 2]) // => -1
R.indexOf tests
import { indexOf } from './indexOf'

test('indexOf', () => {
  expect(indexOf(3, [ 1, 2, 3, 4 ])).toEqual(2)

  expect(indexOf(10)([ 1, 2, 3, 4 ])).toEqual(-1)
})
R.indexOf source
export function indexOf(target, list){
  if (arguments.length === 1) return _list => indexOf(target, _list)

  let index = -1
  const { length } = list

  while (++index < length){
    if (list[ index ] === target){
      return index
    }
  }

  return -1
}

Try in REPL


init

init(arrOrStr: T[]|string): T[]|string

  • It returns all but the last element of arrOrStr.
R.init([1, 2, 3])  // => [1, 2]
R.init('foo')  // => 'fo'
R.init tests
import { compose } from './compose'
import { tail } from './tail'
import { init } from './init'
import { flatten } from './flatten'

test('init', () => {
  expect(
    compose(
      tail,
      init,
      flatten
    )([ [ [ 1, [ 2 ] ] ], [ 3, 4 ] ])
  ).toEqual([ 2, 3 ])

  expect(init([ 1, 2, 3 ])).toEqual([ 1, 2 ])
  expect(init([ 1, 2 ])).toEqual([ 1 ])
  expect(init([ 1 ])).toEqual([])
  expect(init([])).toEqual([])

  expect(init([])).toEqual([])

  expect(init([ 1 ])).toEqual([])

  expect(init('foo')).toEqual('fo')

  expect(init('f')).toEqual('')

  expect(init('')).toEqual('')
})
R.init source
import baseSlice from './internal/baseSlice'

export function init(list){
  if (typeof list === 'string') return list.slice(0, -1)

  return list.length ? baseSlice(list, 0, -1) : []
}

Try in REPL


is

is(xPrototype: any, x: any): boolean

It returns true is x is instance of xPrototype.

R.is(String, 'foo')  // => true
R.is(Array, 1)  // => false
R.is tests
import { is } from './is'

test('works with built-in types', () => {
  expect(is(Array, undefined)).toBeFalsy()
  expect(is(Array)([])).toBeTruthy()
  expect(is(Boolean, new Boolean(false))).toBeTruthy()
  expect(is(Date, new Date())).toBeTruthy()
  expect(is(Function, () => {})).toBeTruthy()
  expect(is(Number, new Number(0))).toBeTruthy()
  expect(is(Object, {})).toBeTruthy()
  expect(is(RegExp, /(?:)/)).toBeTruthy()
  expect(is(String, new String(''))).toBeTruthy()
})

test('works with user-defined types', () => {
  function Foo(){}
  function Bar(){}
  Bar.prototype = new Foo()

  const foo = new Foo()
  const bar = new Bar()

  expect(is(Foo, foo)).toBeTruthy()
  expect(is(Bar, bar)).toBeTruthy()
  expect(is(Foo, bar)).toBeTruthy()
  expect(is(Bar, foo)).toBeFalsy()
})

test('does not coerce', () => {
  expect(is(Boolean, 1)).toBeFalsy()
  expect(is(Number, '1')).toBeFalsy()
  expect(is(Number, false)).toBeFalsy()
})

test('recognizes primitives as their object equivalents', () => {
  expect(is(Boolean, false)).toBeTruthy()
  expect(is(Number, 0)).toBeTruthy()
  expect(is(String, '')).toBeTruthy()
})

test('does not consider primitives to be instances of Object', () => {
  expect(is(Object, false)).toBeFalsy()
  expect(is(Object, 0)).toBeFalsy()
  expect(is(Object, '')).toBeFalsy()
})
R.is source
export function is(ctor, val){
  if (arguments.length === 1) return _val => is(ctor, _val)

  return (
    val != null && val.constructor === ctor ||
    val instanceof ctor
  )
}

Try in REPL


isNil

isNil(x: any): boolean

It returns true is x is either null or undefined.

R.isNil(null)  // => true
R.isNil(1)  // => false
R.isNil tests
import { isNil } from './isNil'

test('', () => {
  expect(isNil(null)).toBeTruthy()

  expect(isNil(undefined)).toBeTruthy()

  expect(isNil([])).toBeFalsy()
})
R.isNil source
export function isNil(x){
  return x === undefined || x === null
}

Try in REPL


isEmpty

isEmpty(x: any): boolean

It returns true is x is empty.

R.isEmpty('')  // => true
R.isEmpty({ x : 0 })  // => false
R.isEmpty tests
import { isEmpty } from './isEmpty'

test('happy', () => {
  expect(isEmpty(undefined)).toEqual(false)
  expect(isEmpty('')).toEqual(true)
  expect(isEmpty(null)).toEqual(false)
  expect(isEmpty(' ')).toEqual(false)
  expect(isEmpty(new RegExp(''))).toEqual(false)
  expect(isEmpty([])).toEqual(true)
  expect(isEmpty([ [] ])).toEqual(false)
  expect(isEmpty({})).toEqual(true)
  expect(isEmpty({ x : 0 })).toEqual(false)
  expect(isEmpty(0)).toEqual(false)
  expect(isEmpty(NaN)).toEqual(false)
  expect(isEmpty([ '' ])).toEqual(false)
})
R.isEmpty source
import { type } from './type.js'

export function isEmpty(input){
  const inputType = type(input)
  if ([ 'Undefined', 'NaN', 'Number', 'Null' ].includes(inputType)) return false
  if (!input) return true

  if (inputType === 'Object'){
    return Object.keys(input).length === 0
  }

  if (inputType === 'Array'){
    return input.length === 0
  }

  return false
}

Try in REPL


join

join(separator: string, arr: T[]): string

R.join('-', [1, 2, 3])  // => '1-2-3'
R.join tests
import { join } from './join'

test('curry', () => {
  expect(join('|')([ 'foo', 'bar', 'baz' ])).toEqual('foo|bar|baz')

  expect(join('|', [ 1, 2, 3 ])).toEqual('1|2|3')

  const spacer = join(' ')

  expect(spacer([ 'a', 2, 3.4 ])).toEqual('a 2 3.4')
})
R.join source
export function join(separator, list){
  if (arguments.length === 1) return _list => join(separator, _list)

  return list.join(separator)
}

Try in REPL


keys

keys(x: Object): string[]

R.keys({a:1, b:2})  // => ['a', 'b']

Source

Try in REPL


last

last(arrOrStr: T[]|string): T|string

  • It returns the last element of arrOrStr.
R.last(['foo', 'bar', 'baz']) // => 'baz'
R.last('foo') // => 'o'
R.last tests
import { compose } from './compose'
import { last } from './last'
import { map } from './map'

test('last', () => {
  expect(
    compose(
      last,
      map(last)
    )([ 'foo', 'bar', 'baz' ])
  ).toEqual('z')

  expect(last([ 'foo', 'bar', 'baz' ])).toEqual('baz')
  expect(last([])).toEqual(undefined)
  expect(last('abc')).toEqual('c')
  expect(last('')).toEqual('')
})
R.last source
export function last(list){
  if (typeof list === 'string') return list[ list.length - 1 ] || ''

  return list[ list.length - 1 ]
}

Try in REPL


lastIndexOf

lastIndexOf(x: any, arr: T[]): number

It returns the last index of x in array arr.

R.equals is used to determine equality between x and members of arr.

Value -1 is returned if no x is found in arr.

R.lastIndexOf(1, [1, 2, 3, 1, 2]) // => 3
R.lastIndexOf(10, [1, 2, 3, 1, 2]) // => -1
R.lastIndexOf tests
import { lastIndexOf } from './lastIndexOf'

test('', () => {
  const a = lastIndexOf(1, [ 1, 2, 3, 1, 2 ])
  const b = lastIndexOf(1)([ 1, 2, 3, 1, 2 ])

  expect(a).toEqual(3)
  expect(b).toEqual(3)
})

test('false', () => {
  const a = lastIndexOf(10, [ 1, 2, 3, 1, 2 ])

  expect(a).toEqual(-1)
})
R.lastIndexOf source
import { equals } from './equals'

export function lastIndexOf(target, list){
  if (arguments.length === 1) return _list => lastIndexOf(target, _list)

  let index = list.length

  while (--index > 0){
    if (equals(list[ index ], target)){
      return index
    }
  }

  return -1
}

Try in REPL


length

length(arrOrStr: Array|String): Number

R.length([1, 2, 3]) // => 3
R.length tests
import { length } from './length'
import { identical } from './identical.js'

test('happy', () => {
  expect(length('foo')).toEqual(3)
  expect(length([ 1, 2, 3 ])).toEqual(3)
  expect(length([])).toEqual(0)
})

test('with bad input', () => {
  expect(identical(NaN, length(0))).toEqual(true)
  expect(identical(NaN, length({}))).toEqual(true)
  expect(identical(NaN, length(null))).toEqual(true)
  expect(identical(NaN, length(undefined))).toEqual(true)
})
R.length source
export function length(list){
  if (list == null || list.length === undefined) return NaN

  return list.length
}

Try in REPL


map

map(mapFn: Function, x: Array|Object): Array|Object

It returns the result of looping through iterable x with mapFn.

The method works with objects as well.

Note that unlike Ramda's map, here array keys are passed as second argument to mapFn.

const mapFn = x => x * 2
const resultWithArray = R.map(mapFn, [1, 2, 3])
// => [2, 4, 6]

const result = R.map((val, prop)=>{
  return `${prop}-${val}`
}, {a: 1, b: 2})
// => {a: 'a-1', b: 'b-2'}
R.map tests
import { map } from './map'
import { add } from './add'
import { compose } from './compose'

const double = x => x * 2

const sampleObject = {
  a : 1,
  b : 2,
  c : 3,
  d : 4,
}

test('with array', () => {
  expect(map(double, [ 1, 2, 3 ])).toEqual([ 2, 4, 6 ])
})

test('pass index as second argument', () => {
  let counter = 0
  map(
    (x, i) => {
      expect(i).toBe(counter)
      counter++
    },
    [ 10, 20, 30 ]
  )
})

test('with object', () => {
  const obj = {
    a : 1,
    b : 2,
  }

  expect(map(double, obj)).toEqual({
    a : 2,
    b : 4,
  })
})

test('pass input object as third argument', () => {
  const obj = {
    a : 1,
    b : 2,
  }
  const iterator = (val, prop, inputObject) => {
    expect(inputObject).toEqual(obj)

    return val * 2
  }
  expect(map(iterator, obj)).toEqual({
    a : 2,
    b : 4,
  })
})

test('with object passes property as second argument', () => {
  map((_, prop) => {
    expect(typeof prop).toEqual('string')
  })(sampleObject)
})

/**
 * https://github.com/selfrefactor/rambda/issues/77
 */
test('when undefined instead of array', () => {
  expect(map(double, undefined)).toEqual([])
})

test('with R.compose', () => {
  const result = compose(
    map(add(1)),
    map(add(1))
  )([ 1, 2, 3 ])
  expect(result).toEqual([ 3, 4, 5 ])
})
R.map source
function mapObject(fn, obj){
  const willReturn = {}

  for (const prop in obj){
    willReturn[ prop ] = fn(obj[ prop ], prop, obj)
  }

  return willReturn
}

export function map(fn, list){
  if (arguments.length === 1) return _list => map(fn, _list)

  if (list === undefined){
    return []
  }
  if (!Array.isArray(list)){
    return mapObject(fn, list)
  }

  let index = -1
  const len = list.length
  const willReturn = Array(len)

  while (++index < len){
    willReturn[ index ] = fn(list[ index ], index)
  }

  return willReturn
}

Try in REPL


match

match(regExpression: Regex, str: string): string[]

R.match(/([a-z]a)/g, 'bananas') // => ['ba', 'na', 'na']
R.match tests
import { match } from './match'

test('', () => {
  expect(match(/a./g)('foo bar baz')).toEqual([ 'ar', 'az' ])

  expect(match(/a./g)('foo')).toEqual([])

  expect(() => {
    match(/a./g, null)
  }).toThrow()
})
R.match source
export function match(pattern, str){
  if (arguments.length === 1) return _str => match(pattern, _str)

  const willReturn = str.match(pattern)

  return willReturn === null ? [] : willReturn
}

Try in REPL


max

max(x: Number|String, y: Number|String): Number|String

R.max(5,7) // => 7
R.max tests
import { max } from './max'

test('max', () => {
  expect(max(2, 1)).toBe(2)
})
R.max source
export function max(a, b){
  if (arguments.length === 1) return _b => max(a, _b)

  return b > a ? b : a
}

Try in REPL


maxBy

maxBy(fn: Function, x: Number|String, y: Number|String): Number|String

R.maxBy(Math.abs, 5, -7) // => -7
R.maxBy tests
import { maxBy } from './maxBy'

test('1', () => {
  expect(maxBy(Math.round, 0.66, 0.77)).toEqual(0.66)
})

test('2', () => {
  expect(maxBy(Math.round, 0.77, 0.66)).toEqual(0.77)
})

test('3', () => {
  expect(maxBy(Math.round)(0.77, 0.66)).toEqual(0.77)
})

test('4', () => {
  expect(maxBy(Math.round, 0.77)(0.66)).toEqual(0.77)
})

test('5', () => {
  expect(maxBy(x => x === 1 ? -1 : 1, 1, 0.66)).toEqual(0.66)
})
R.maxBy source
export function maxBy(fn, a, b){
  if (arguments.length === 2){
    return _b => maxBy(fn, a, _b)
  } else if (arguments.length === 1){
    return (_a, _b) => maxBy(fn, _a, _b)
  }

  return fn(b) > fn(a) ? b : a
}

Try in REPL


merge

merge(a: Object, b: Object)

It returns result of Object.assign({}, a, b).

R.merge({ 'foo': 0, 'bar': 1 }, { 'foo': 7 })
// => { 'foo': 7, 'bar': 1 }
R.merge tests
import { merge } from './merge'

const sample = {
  foo : 'bar',
  bar : 'bar',
}

test('merge', () => {
  expect(merge(sample)({ bar : 'baz' })).toEqual({
    foo : 'bar',
    bar : 'baz',
  })
})

/**
 * https://github.com/selfrefactor/rambda/issues/77
 */
test('when undefined or null instead of object', () => {
  expect(merge(null, undefined)).toEqual({})
  expect(merge(sample, null)).toEqual(sample)
  expect(merge(sample, undefined)).toEqual(sample)
  expect(merge(undefined, sample)).toEqual(sample)
})
R.merge source
export function merge(obj, props){
  if (arguments.length === 1) return _props => merge(obj, _props)

  return Object.assign({}, obj || {}, props || {})
}

Try in REPL


min

min(x: Number|String, y: Number|String): Number|String

R.max(5,7) // => 5
R.min tests
import { min } from './min'

test('', () => {
  expect(min(2, 1)).toBe(1)
  expect(min(2)(1)).toBe(1)
})
R.min source
export function min(a, b){
  if (arguments.length === 1) return _b => min(a, _b)

  return b < a ? b : a
}

Try in REPL


minBy

minBy(fn: Function, x: Number|String, y: Number|String): Number|String

R.minBy(Math.abs, -5, -7) // => -5
R.minBy tests
import { minBy } from './minBy'

test('1', () => {
  expect(minBy(Math.round, 0.66, 0.77)).toEqual(0.66)
})

test('2', () => {
  expect(minBy(Math.round, 0.77, 0.66)).toEqual(0.77)
})

test('3', () => {
  expect(minBy(Math.round)(0.77, 0.66)).toEqual(0.77)
})

test('4', () => {
  expect(minBy(Math.round, 0.77)(0.66)).toEqual(0.77)
})

test('5', () => {
  expect(minBy(x => x === 1 ? -1 : 1, 1, 0.66)).toEqual(1)
})
R.minBy source
export function minBy(fn, a, b){
  if (arguments.length === 2){
    return _b => minBy(fn, a, _b)
  } else if (arguments.length === 1){
    return (_a, _b) => minBy(fn, _a, _b)
  }

  return fn(b) < fn(a) ? b : a
}

Try in REPL


modulo

modulo(a: number, b: number):numberNumber

It returns the remainder of operation a/b.

R.module(14, 3) // => 2
R.modulo tests
import { modulo } from './modulo'

test('', () => {
  expect(modulo(17, 3)).toEqual(2)
  expect(modulo(15)(6)).toEqual(3)
})
R.modulo source
export function modulo(a, b){
  if (arguments.length === 1) return _b => modulo(a, _b)

  return a % b
}

Try in REPL


multiply

multiply(a: number, b: number): number

It returns the result of operation a*b.

R.multiply(4, 3) // => 12
R.multiply tests
import { multiply } from './multiply'

test('', () => {
  expect(multiply(2, 4)).toEqual(8)
  expect(multiply(2)(4)).toEqual(8)
})
R.multiply source
export function multiply(a, b){
  if (arguments.length === 1) return _b => multiply(a, _b)

  return a * b
}

Try in REPL


not

not(x: any): boolean

It returns inverted boolean version of input x.

R.not(true) //=> false
R.not(false) //=> true
R.not(0) //=> true
R.not(1) //=> false
R.not tests
import { not } from './not'

test('not', () => {
  expect(not(false)).toEqual(true)
  expect(not(true)).toEqual(false)
  expect(not(0)).toEqual(true)
  expect(not(1)).toEqual(false)
})
R.not source
export function not(a){
  return !a
}

Try in REPL


omit

omit(propsToOmit: string[]|string, obj: Object): Object

It returns a partial copy of an obj with omitting propsToOmit

R.omit('a,c,d', {a: 1, b: 2, c: 3}) // => {b: 2}
R.omit tests
import { omit } from './omit'

test('with string as condition', () => {
  const obj = {
    a : 1,
    b : 2,
    c : 3,
  }
  const result = omit('a,c', obj)
  const resultCurry = omit('a,c')(obj)
  const expectedResult = { b : 2 }

  expect(result).toEqual(expectedResult)
  expect(resultCurry).toEqual(expectedResult)
})

test('with null', () => {
  expect(omit('a,b', null)).toEqual(undefined)
})

test('doesn\'t work with number as property', () => {
  expect(
    omit([ 42 ], {
      a  : 1,
      42 : 2,
    })
  ).toEqual({
    42 : 2,
    a  : 1,
  })
})

test('', () => {
  expect(
    omit([ 'a', 'c' ])({
      a : 'foo',
      b : 'bar',
      c : 'baz',
    })
  ).toEqual({ b : 'bar' })
})
R.omit source
export function omit(keys, obj){
  if (arguments.length === 1) return _obj => omit(keys, _obj)

  if (obj === null || obj === undefined){
    return undefined
  }

  const keysValue =
    typeof keys === 'string' ? keys.split(',') : keys

  const willReturn = {}

  for (const key in obj){
    if (!keysValue.includes(key)){
      willReturn[ key ] = obj[ key ]
    }
  }

  return willReturn
}

Try in REPL


path

path(pathToSearch: string[]|string, obj: Object): any

If pathToSearch is 'a.b' then it will return 1 if obj is {a:{b:1}}.

It will return undefined, if such path is not found.

R.path('a.b', {a: {b: 1}}) // => 1
R.path tests
import { path } from './path'

test('with array inside object', () => {
  const obj = { a : { b : [ 1, { c : 1 } ] } }

  expect(path('a.b.1.c', obj)).toBe(1)
})

test('works with undefined', () => {
  const obj = { a : { b : { c : 1 } } }

  expect(path('a.b.c.d.f', obj)).toBeUndefined()
  expect(path('foo.babaz', undefined)).toBeUndefined()
  expect(path('foo.babaz')(undefined)).toBeUndefined()
})

test('works with string instead of array', () => {
  expect(
    path('foo.bar.baz')({ foo : { bar : { baz : 'yes' } } })
  ).toEqual('yes')
})

test('path', () => {
  expect(
    path([ 'foo', 'bar', 'baz' ])({ foo : { bar : { baz : 'yes' } } })
  ).toEqual('yes')

  expect(path([ 'foo', 'bar', 'baz' ])(null)).toBeUndefined()

  expect(
    path([ 'foo', 'bar', 'baz' ])({ foo : { bar : 'baz' } })
  ).toBeUndefined()
})
R.path source
export function path(list, obj){
  if (arguments.length === 1) return _obj => path(list, _obj)

  if (obj === null || obj === undefined){
    return undefined
  }
  let willReturn = obj
  let counter = 0

  const pathArrValue = typeof list === 'string' ? list.split('.') : list

  while (counter < pathArrValue.length){
    if (willReturn === null || willReturn === undefined){
      return undefined
    }
    willReturn = willReturn[ pathArrValue[ counter ] ]
    counter++
  }

  return willReturn
}

Try in REPL


pathOr

pathOr(defaultValue: any, pathToSearch: string[]|string, obj: Object): any

pathFound is the result of calling R.path(pathToSearch, obj).

If pathFound is undefined, null or NaN, then defaultValue will be returned.

pathFound is returned in any other case.

R.pathOr(1, 'a.b', {a: {b: 2}}) // => 2
R.pathOr(1, ['a', 'b'], {a: {b: 2}}) // => 2
R.pathOr(1, ['a', 'c'], {a: {b: 2}}) // => 1
R.pathOr tests
import { pathOr } from './pathOr'

test('with undefined', () => {
  const result = pathOr('foo', 'x.y', { x : { y : 1 } })

  expect(result).toEqual(1)
})

test('with null', () => {
  const result = pathOr('foo', 'x.y', null)

  expect(result).toEqual('foo')
})

test('with NaN', () => {
  const result = pathOr('foo', 'x.y', NaN)

  expect(result).toEqual('foo')
})

test('curry case (x)(y)(z)', () => {
  const result = pathOr('foo')('x.y.z')({ x : { y : { a : 1 } } })

  expect(result).toEqual('foo')
})

test('curry case (x)(y,z)', () => {
  const result = pathOr('foo', 'x.y.z')({ x : { y : { a : 1 } } })

  expect(result).toEqual('foo')
})

test('curry case (x,y)(z)', () => {
  const result = pathOr('foo')('x.y.z', { x : { y : { a : 1 } } })

  expect(result).toEqual('foo')
})
R.pathOr source
import { curry } from './curry'
import { defaultTo } from './defaultTo'
import { path } from './path'

export const pathOr = curry(pathOrRaw)

Try in REPL


partial

partial(fn: Function, ...inputs: any[]): Function | any

It is very similar to R.curry, but you can pass initial arguments when you create the curried function.

R.partial will keep returning a function until all the arguments that the function fn expects are passed. The name comes from the fact that you partially inject the inputs.

const fn = (salutation, title, firstName, lastName) => salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!'

const canPassAnyNumberOfArguments = partial(fn, 'Hello', 'Ms.')
const finalFn = canPassAnyNumberOfArguments('foo')

finalFn('bar') // =>  'Hello, Ms. foo bar!'

partialCurry

partialCurry(fn: Function|Async, a: Object, b: Object): Function|Promise

When called with function fn and first set of input a, it will return a function.

This function will wait to be called with second set of input b and it will invoke fn with the merged object of a over b.

fn can be asynchronous function. In that case a Promise holding the result of fn is returned.

See the example below:

const fn = ({a, b, c}) => {
  return (a * b) + c
}
const curried = R.partialCurry(fn, {a: 2})
const result = curried({b: 3, c: 10})
// => 16
R.partialCurry tests
import { type } from './type'
import { partialCurry } from './partialCurry'

test('', () => {
  const fn = ({ a, b, c }) => a + b + c
  const curried = partialCurry(fn, { a : 1 })

  expect(type(curried)).toEqual('Function')
  expect(
    curried({
      b : 2,
      c : 3,
    })
  ).toEqual(6)
  expect(true).toBeTruthy()
})

test('with promise', done => {
  const delay = ({ ms, x }) =>
    new Promise(resolve => {
      setTimeout(() => {
        resolve(x * 2)
      }, ms)
    })

  const curried = partialCurry(delay, { ms : 200 })

  curried({ x : 3 }).then(result => {
    expect(type(curried)).toEqual('Function')
    done()
  })
})

test('with async', async () => {
  const delay = ms =>
    new Promise(resolve => {
      setTimeout(() => {
        resolve()
      }, ms)
    })

  const fn = async ({ a, b, c }) => {
    await delay(100)

    return a + b + c
  }

  const curried = partialCurry(fn, { a : 1 })

  const result = await curried({
    b : 2,
    c : 3,
  })

  expect(result).toEqual(6)
})
R.partialCurry source
import { merge } from './merge'
import { type } from './type'

export function partialCurry(fn, args = {}){
  return rest => {
    if (type(fn) === 'Async' || type(fn) === 'Promise'){
      return new Promise((resolve, reject) => {
        fn(merge(rest, args))
          .then(resolve)
          .catch(reject)
      })
    }

    return fn(merge(rest, args))
  }
}
  • Note that partialCurry is method specific for Rambda and the method is not part of Ramda's API

  • You can read my argumentation for creating partialCurry here

Try in REPL


pick

pick(propsToPick: string[], obj: Object): Object

It returns a partial copy of an obj containing only propsToPick properties.

R.pick(['a', 'c'], {a: 1, b: 2}) // => {a: 1}
R.pick tests
import { pick } from './pick'

test('pick with string as condition', () => {
  const obj = {
    a : 1,
    b : 2,
    c : 3,
  }
  const result = pick('a,c', obj)
  const resultCurry = pick('a,c')(obj)
  const expectedResult = {
    a : 1,
    c : 3,
  }

  expect(result).toEqual(expectedResult)
  expect(resultCurry).toEqual(expectedResult)
})

test('pick', () => {
  expect(
    pick([ 'a', 'c' ])({
      a : 'foo',
      b : 'bar',
      c : 'baz',
    })
  ).toEqual({
    a : 'foo',
    c : 'baz',
  })

  expect(
    pick([ 'a', 'd', 'e', 'f' ])({
      a : 'foo',
      b : 'bar',
      c : 'baz',
    })
  ).toEqual({ a : 'foo' })

  expect(pick('a,d,e,f')(null)).toEqual(undefined)
})
R.pick source
export function pick(keys, obj){
  if (arguments.length === 1) return _obj => pick(keys, _obj)

  if (obj === null || obj === undefined){
    return undefined
  }
  const keysValue =
    typeof keys === 'string' ? keys.split(',') : keys

  const willReturn = {}
  let counter = 0

  while (counter < keysValue.length){
    if (keysValue[ counter ] in obj){
      willReturn[ keysValue[ counter ] ] = obj[ keysValue[ counter ] ]
    }
    counter++
  }

  return willReturn
}

Try in REPL


pipe

pipe(fn1: Function, ... , fnN: Function): any

It performs left-to-right function composition.

const result = R.pipe(
  R.filter(val => val > 2),
  R.map(a => a * 2)
)([1, 2, 3, 4])

// => [6, 8]
R.pipe tests
import { pipe } from './pipe'
import { map } from './map'
import { add } from './add'
import { last } from './last'

test('pipe', () => {
  const result = pipe(
    map(add(1)),
    map(add(10)),
    last
  )([ 1, 2, 3 ])

  expect(result).toEqual(14)
})

test('with bad input', () => {
  expect(
    () => pipe()
  ).toThrow(
    'pipe requires at least one argument'
  )
})
R.pipe source
import { compose } from './compose'

export function pipe(...fns){
  if (fns.length === 0) throw new Error('pipe requires at least one argument')

  return compose(...fns.reverse())
}

Try in REPL


pluck

pluck(property: string, arr: Object[]): any[]

It returns list of the values of property taken from the objects in array of objects arr.

R.pluck('a')([{a: 1}, {a: 2}, {b: 3}]) // => [1, 2]
R.pluck tests
import { pluck } from './pluck'

test('', () => {
  expect(pluck('a')([ { a : 1 }, { a : 2 }, { b : 1 } ])).toEqual([ 1, 2 ])
})

test('with number', () => {
  const input = [ [ 1, 2 ], [ 3, 4 ] ]

  expect(pluck(0, input)).toEqual([ 1, 3 ])
})
R.pluck source
import { map } from './map'

export function pluck(key, list){
  if (arguments.length === 1) return _list => pluck(key, _list)

  const willReturn = []

  map(val => {
    if (val[ key ] !== undefined){
      willReturn.push(val[ key ])
    }
  }, list)

  return willReturn
}

Try in REPL


prepend

prepend(x: T, arr: T[]): T[]

It adds x to the start of the array arr.

R.prepend('foo', ['bar', 'baz']) // => ['foo', 'bar', 'baz']
R.prepend tests
import { prepend } from './prepend'

test('', () => {
  expect(prepend('f', 'oo')).toEqual('foo')
})

test('prepend', () => {
  expect(prepend('yes', [ 'foo', 'bar', 'baz' ])).toEqual([
    'yes',
    'foo',
    'bar',
    'baz',
  ])

  expect(prepend('foo')([])).toEqual([ 'foo' ])
})
R.prepend source
export function prepend(el, list){
  if (arguments.length === 1) return _list => prepend(el, _list)

  if (typeof list === 'string') return `${ el }${ list }`

  const clone = [ el ].concat(list)

  return clone
}

Try in REPL


prop

prop(propToFind: string, obj: Object): any

It returns undefined or the value of property propToFind in obj

R.prop('x', {x: 100}) // => 100
R.prop('x', {a: 1}) // => undefined
R.prop tests
import { prop } from './prop'

test('prop', () => {
  expect(prop('foo')({ foo : 'baz' })).toEqual('baz')

  expect(prop('bar')({ foo : 'baz' })).toEqual(undefined)

  expect(prop('bar')(null)).toEqual(undefined)
})
R.prop source
export function prop(key, obj){
  if (arguments.length === 1) return _obj => prop(key, _obj)

  if (!obj) return undefined

  return obj[ key ]
}

Try in REPL


propEq

propEq(propToFind: string, valueToMatch: any, obj: Object): boolean

It returns true if obj has property propToFind and its value is equal to valueToMatch.

const propToFind = 'foo'
const valueToMatch = 0

const result = R.propEq(propToFind, valueToMatch)({foo: 0})
// => true
R.propEq tests
import { propEq } from './propEq'

test('propEq', () => {
  expect(propEq('foo', 'bar')({ foo : 'bar' })).toBeTruthy()

  expect(propEq('foo', 'bar')({ foo : 'baz' })).toBeFalsy()

  expect(propEq('foo')('bar')({ foo : 'baz' })).toBeFalsy()
})

test('happy', () => {
  expect(propEq('name', 'Abby', null)).toEqual(false)
// expect(propEq('name', 'Abby', undefined)).toEqual(false)
})
R.propEq source
import { curry } from './curry'

export const propEq = curry(propEqFn)

Try in REPL


propIs

propIs(type: any, name: string, obj: Object): boolean

It Returns true if the specified object property is of the given type.

R.propIs(Number, 'x', {x: 1, y: 2});  //=> true
R.propIs(Number, 'x', {x: 'foo'});    //=> false
R.propIs(Number, 'x', {});            //=> false
R.propIs tests
import { propIs } from './propIs'

test('1', () => {
  expect(propIs(Number, 'value', { value : 1 })).toEqual(true)
})

test('2', () => {
  expect(propIs(String, 'value', { value : 1 })).toEqual(false)
})

test('3', () => {
  expect(propIs(String)('value')({})).toEqual(false)
})
R.propIs source
import { is } from './is'
import { curry } from './curry.js'

export const propIs = curry(propIsFn)

Try in REPL


propOr

propOr(defaultValue: any, param: string, obj: Object): any

If the given, non-null object has an own property with the specified name, returns the value of that property. Otherwise returns the provided default value.

const theWall = { mother: 'Waters', comfortablyNumb: 'Gilmour/Waters' }
const authorOfWishYouWereHere = R.prop('wishYouWereHere')
const authorOfAtomHeartMotherWhenDefault = R.propOr('Pink Floyd', 'atomHeartMother')

authorOfWishYouWereHere(theWall)  //=> undefined
authorOfAtomHeartMotherWhenDefault(theWall) //=> 'Pink Floyd'

range

range(start: number, end: number): number[]

It returns a array of numbers from start(inclusive) to end(exclusive).

R.range(0, 3)   // => [0, 1, 2]
R.range tests
import { range } from './range'

test('happy', () => {
  expect(range(0, 10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])
})

test('end range is bigger than start range', () => {
  expect(range(7, 3)).toEqual([])
  expect(range(5, 5)).toEqual([])
})

test('with bad input', () => {
  expect(
    () => range('a', 6)
  ).toThrow('Both arguments to range must be numbers')
  expect(
    () => range(6, 'z')
  ).toThrow('Both arguments to range must be numbers')

})

test('curry', () => {
  expect(range(0)(10)).toEqual([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ])
})
R.range source
export function range(from, to){
  if (arguments.length === 1) return _to => range(from, _to)

  if (Number.isNaN(Number(from)) || Number.isNaN(Number(to))){
    throw new TypeError('Both arguments to range must be numbers')
  }

  if (to < from) return []

  const len = to - from
  const willReturn = Array(len)

  for (let i = 0; i < len; i++){
    willReturn[ i ] = from + i
  }

  return willReturn
}

Try in REPL


reduce

reduce(iteratorFn: Function, accumulator: any, array: T[]): any

const iteratorFn = (acc, val) => acc + val
const result = R.reduce(iteratorFn, 1, [1, 2, 3])
// => 7
R.reduce tests
import { compose } from './compose'
import { reduce } from './reduce'
import { map } from './map'
import { curry } from './curry'

test('happy', () => {
  const result = reduce((acc, val, i) => {
    expect(typeof i).toBe('number')

    return acc + val
  })(1)([ 1, 2, 3 ])

  expect(result).toEqual(7)
})

test('with compose', () => {
  const convertToString = (acc, value) => acc + value

  expect(
    compose(
      reduce(convertToString, ''),
      map(x => x + 1)
    )([ 1, 2, 3 ])
  ).toEqual('234')
})

test('with curry', () => {
  const add = curry((n, n2) => n + n2)

  expect(reduce(add, 0, [ 1, 2, 3 ])).toEqual(6)
})
R.reduce source
import { curry } from './curry'

export const reduce = curry(reduceFn)

Try in REPL


reject

reject(fn: Function, arr: T[]): T[]

It has the opposite effect of R.filter.

It will return those members of arr that return false when applied to function fn.

const fn = x => x % 2 === 1

const result = R.reject(fn, [1, 2, 3, 4])
// => [2, 4]
R.reject tests
import { reject } from './reject'
import { compose } from './compose'
import { add } from './add'
import { map } from './map'
import { equals } from './equals'

const isOdd = n => n % 2 === 1

test('with array', () => {
  expect(reject(isOdd, [ 1, 2, 3, 4 ])).toEqual([ 2, 4 ])
})

test('with object', () => {
  expect(
    reject(isOdd, {
      a : 1,
      b : 2,
      c : 3,
      d : 4,
    })
  ).toEqual({
    b : 2,
    d : 4,
  })
})

test('should work with currying', () => {
  const result = compose(
    reject(equals(2)),
    map(add(1))
  )({
    a : 1,
    b : 2,
    c : 3,
  })

  expect(result).toEqual({
    b : 3,
    c : 4,
  })
})

test('pass index as second argument', () => {
  reject(
    (x, i) => {
      expect(typeof x).toBe('number')
      expect(typeof i).toBe('number')
    }
  )([ 10, 12, 15 ])
})
R.reject source
import { filter } from './filter'

export function reject(fn, list){
  if (arguments.length === 1) return _list => reject(fn, _list)

  return filter((x, i) => !fn(x, i), list)
}

Try in REPL


repeat

repeat(valueToRepeat: T, num: number): T[]

R.repeat('foo', 2) // => ['foo', 'foo']
R.repeat tests
import { repeat } from './repeat'

test('repeat', () => {
  expect(repeat('')(3)).toEqual([ '', '', '' ])
  expect(repeat('foo', 3)).toEqual([ 'foo', 'foo', 'foo' ])

  const obj = {}
  const arr = repeat(obj, 3)

  expect(arr).toEqual([ {}, {}, {} ])

  expect(arr[ 0 ] === arr[ 1 ]).toBeTruthy()
})
R.repeat source
export function repeat(val, n){
  if (arguments.length === 1) return _n => repeat(val, _n)

  const willReturn = Array(n)

  return willReturn.fill(val)
}

Try in REPL


replace

replace(strOrRegex: string|Regex, replacer: string, str: string): string

It replaces strOrRegex found in str with replacer.

R.replace('foo', 'bar', 'foo foo') // => 'bar foo'
R.replace(/foo/, 'bar', 'foo foo') // => 'bar foo'
R.replace(/foo/g, 'bar', 'foo foo') // => 'bar bar'
R.replace tests
import { replace } from './replace'

test('', () => {
  expect(replace('foo', 'yes', 'foo bar baz')).toEqual(
    'yes bar baz'
  )

  expect(replace(/\s/g)('|')('foo bar baz')).toEqual('foo|bar|baz')
  expect(replace(/\s/g)('|', 'foo bar baz')).toEqual('foo|bar|baz')
  expect(replace(/\s/g, '|')('foo bar baz')).toEqual('foo|bar|baz')
})
R.replace source
export function replace(pattern, replacer, str){
  if (replacer === undefined){
    return (_replacer, _str) => replace(pattern, _replacer, _str)
  } else if (str === undefined){
    return _str => replace(pattern, replacer, _str)
  }

  return str.replace(pattern, replacer)
}

Try in REPL


reverse

reverse(str: T[]): T[]

const arr = [1, 2]

const result = R.reverse(arr)
// => [2, 1]
R.reverse tests
import { reverse } from './reverse'

test('happy', () => {
  expect(reverse([ 1, 2, 3 ])).toEqual([ 3, 2, 1 ])
})

test('with string', () => {
  expect(reverse('baz')).toEqual('zab')
})

test('it doesn\'t mutate', () => {
  const arr = [ 1, 2, 3 ]

  expect(reverse(arr)).toEqual([ 3, 2, 1 ])

  expect(arr).toEqual([ 1, 2, 3 ])
})
R.reverse source
export function reverse(input){
  if (typeof input === 'string'){
    return input.split('').reverse()
      .join('')
  }

  const clone = input.slice()

  return clone.reverse()
}

Try in REPL


slice

slice(list: T[], from: Number, to: Number)

Returns the elements of the given list or string (or object with a slice method) from fromIndex (inclusive) to toIndex (exclusive). Dispatches to the slice method of the third argument, if present.

R.slice(1, 3, ['a', 'b', 'c', 'd'])
//=> ['b', 'c']

sort

sort(sortFn: Function, arr: T[]): T[]

It returns copy of arr sorted by sortFn.

Note that sortFn must return a number type.

const sortFn = (a, b) => a - b

const result = R.sort(sortFn, [3, 1, 2])
// => [1, 2, 3]
R.sort tests
import { sort } from './sort'

const fn = (a, b) => a > b ? 1 : -1

test('sort', () => {
  expect(sort((a, b) => a - b)([ 2, 3, 1 ])).toEqual([ 1, 2, 3 ])
})

test('it doesn\'t mutate', () => {
  const list = [ 'foo', 'bar', 'baz' ]

  expect(sort(fn, list)).toEqual([
    'bar',
    'baz',
    'foo',
  ])

  expect(list[ 0 ]).toBe('foo')
  expect(list[ 1 ]).toBe('bar')
  expect(list[ 2 ]).toBe('baz')
})
R.sort source
export function sort(fn, list){
  if (arguments.length === 1) return _list => sort(fn, _list)

  const arrClone = list.slice()

  return arrClone.sort(fn)
}

Try in REPL


sortBy

sortBy(sortFn: Function, arr: T[]): T[]

It returns copy of arr sorted by sortFn.

Note that sortFn must return value for comparison.

const sortFn = obj => obj.foo

const result = R.sortBy(sortFn, [
  {foo: 1},
  {foo: 0}
])

const expectedResult = [ {foo: 0}, {foo: 1} ]
console.log(R.equals(result, expectedResult))
// => true
R.sortBy tests
import { compose } from './compose'
import { toLower } from './toLower'
import { prop } from './prop'
import { sortBy } from './sortBy'

test('sortBy', () => {
  const sortByNameCaseInsensitive = sortBy(
    compose(
      toLower,
      prop('name')
    )
  )
  const alice = {
    name : 'ALICE',
    age  : 101,
  }
  const bob = {
    name : 'Bob',
    age  : -10,
  }
  const clara = {
    name : 'clara',
    age  : 314.159,
  }
  const people = [ clara, bob, alice ]

  expect(sortByNameCaseInsensitive(people)).toEqual([
    alice,
    bob,
    clara,
  ])

  expect(
    sortBy(val => val.a, [ { a : 2 }, { a : 1 }, { a : 0 } ])
  ).toEqual([ { a : 0 }, { a : 1 }, { a : 2 } ])

  expect(
    sortBy(val => val.a, [ { a : 1 }, { a : 1 }, { a : 1 } ])
  ).toEqual([ { a : 1 }, { a : 1 }, { a : 1 } ])

  expect(
    sortBy(val => val.a, [ { a : 3 }, { a : 2 }, { a : 1 } ])
  ).toEqual([ { a : 1 }, { a : 2 }, { a : 3 } ])

  expect(
    sortBy(val => val.a, [ { a : 1 }, { a : 2 }, { a : 3 } ])
  ).toEqual([ { a : 1 }, { a : 2 }, { a : 3 } ])
})
R.sortBy source
export function sortBy(fn, list){
  if (arguments.length === 1) return _list => sortBy(fn, _list)

  const arrClone = list.slice()

  return arrClone.sort((a, b) => {
    const fnA = fn(a)
    const fnB = fn(b)

    if (fnA === fnB) return 0

    return fnA < fnB ? -1 : 1
  })
}

Try in REPL


split

split(separator: string, str: string): string[]

R.split('-', 'a-b-c') // => ['a', 'b', 'c']
R.split tests
import { split } from './split'

test('split', () => {
  expect(split('|')('foo|bar|baz')).toEqual([ 'foo', 'bar', 'baz' ])

  expect(split('.', 'a.b.c.xyz.d')).toEqual([
    'a',
    'b',
    'c',
    'xyz',
    'd',
  ])
})
R.split source
export function split(separator, str){
  if (arguments.length === 1) return _str => split(separator, _str)

  return str.split(separator)
}

Try in REPL


splitEvery

splitEvery(sliceLength: number, arrOrString: T[]|string): T[T[]]|string[]

  • It splits arrOrStr into slices of sliceLength.
R.splitEvery(2, [1, 2, 3]) // => [[1, 2], [3]]
R.splitEvery(3, 'foobar') // => ['foo', 'bar']
R.splitEvery tests
import { splitEvery } from './splitEvery'

test('', () => {
  expect(splitEvery(3, [ 1, 2, 3, 4, 5, 6, 7 ])).toEqual([
    [ 1, 2, 3 ],
    [ 4, 5, 6 ],
    [ 7 ],
  ])

  expect(splitEvery(3)('foobarbaz')).toEqual([ 'foo', 'bar', 'baz' ])
})

test('with bad input', () => {
  expect(
    () => expect(splitEvery(0)('foo')).toEqual([ 'f', 'o', 'o' ])
  ).toThrow('First argument to splitEvery must be a positive integer')

})
R.splitEvery source
export function splitEvery(n, list){
  if (arguments.length === 1) return _list => splitEvery(n, _list)

  if (n < 1) throw new Error('First argument to splitEvery must be a positive integer')
  const willReturn = []
  let counter = 0

  while (counter < list.length){
    willReturn.push(list.slice(counter, counter += n))
  }

  return willReturn
}

Try in REPL


startsWith

startsWith(x: string, str: string): boolean

R.startsWith(
  'foo',
  'foo-bar'
) // => true

R.startsWith(
  'bar',
  'foo-bar'
) // => false
R.startsWith tests
import { startsWith } from './startsWith'

test('true', () => {
  const result = startsWith('foo', 'foo-bar')

  expect(result).toBeTruthy()
})

test('false', () => {
  const result = startsWith('baz')('foo-bar')

  expect(result).toBeFalsy()
})
R.startsWith source
export function startsWith(prefix, list){
  if (arguments.length === 1) return _list => startsWith(prefix, _list)

  return list.startsWith(prefix)
}

Try in REPL


subtract

subtract(a: number, b: number): number

R.subtract(3, 1) // => 2
R.subtract tests
import { subtract } from './subtract'

test('', () => {
  expect(subtract(2, 1)).toEqual(1)
  expect(subtract(2)(1)).toEqual(1)
})
R.subtract source
export function subtract(a, b){
  if (arguments.length === 1) return _b => subtract(a, _b)

  return a - b
}

Try in REPL


sum

sum(listOfNumbers: number[]): number

R.sum([1,2,3,4,5]) // => 15

T

R.T() // => true

Source


tail

tail(arrOrStr: T[]|string): T[]|string

  • It returns all but the first element of arrOrStr
R.tail([1, 2, 3])  // => [2, 3]
R.tail('foo')  // => 'oo'
R.tail tests
import { tail } from './tail'

test('tail', () => {
  expect(tail([ 1, 2, 3 ])).toEqual([ 2, 3 ])
  expect(tail([ 1, 2 ])).toEqual([ 2 ])
  expect(tail([ 1 ])).toEqual([])
  expect(tail([])).toEqual([])

  expect(tail('abc')).toEqual('bc')
  expect(tail('ab')).toEqual('b')
  expect(tail('a')).toEqual('')
  expect(tail('')).toEqual('')
})
R.tail source
import { drop } from './drop'

export function tail(list){
  return drop(1, list)
}

Try in REPL


take

take(num: number, arrOrStr: T[]|string): T[]|string

  • It returns the first num elements of arrOrStr.
R.take(1, ['foo', 'bar']) // => ['foo']
R.take(2, 'foo') // => 'fo'
R.take tests
import R from 'ramda'
import { take } from './take'

test('happy', () => {
  const arr = [ 'foo', 'bar', 'baz' ]

  expect(take(1, arr)).toEqual([ 'foo' ])

  expect(arr).toEqual([ 'foo', 'bar', 'baz' ])

  expect(take(2)([ 'foo', 'bar', 'baz' ])).toEqual([ 'foo', 'bar' ])
  expect(take(3, [ 'foo', 'bar', 'baz' ])).toEqual([
    'foo',
    'bar',
    'baz',
  ])
  expect(take(4, [ 'foo', 'bar', 'baz' ])).toEqual([
    'foo',
    'bar',
    'baz',
  ])
  expect(take(3)('rambda')).toEqual('ram')
})

test('with negative index', () => {
  console.log(
    take(1, [ [ 1, 2, 3 ] ]),
    R.take(1, [ [ 1, 2, 3 ] ]),
  )
  expect(take(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
  expect(take(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})

test('with zero index', () => {
  expect(take(0, [ 1, 2, 3 ])).toEqual([])
})
R.take source
import baseSlice from './internal/baseSlice'

export function take(n, list){
  if (arguments.length === 1) return _list => take(n, _list)
  if (n < 0) return list.slice()
  if (typeof list === 'string') return list.slice(0, n)

  return baseSlice(list, 0, n)
}

Try in REPL


takeLast

takeLast(num: number, arrOrStr: T[]|string): T[]|string

  • It returns the last num elements of arrOrStr.
R.takeLast(1, ['foo', 'bar']) // => ['bar']
R.takeLast(2, 'foo') // => 'oo'
R.takeLast tests
import { takeLast } from './takeLast'

test('with arrays', () => {
  expect(takeLast(1, [ 'foo', 'bar', 'baz' ])).toEqual([ 'baz' ])

  expect(takeLast(2)([ 'foo', 'bar', 'baz' ])).toEqual([
    'bar',
    'baz',
  ])

  expect(takeLast(3, [ 'foo', 'bar', 'baz' ])).toEqual([
    'foo',
    'bar',
    'baz',
  ])

  expect(takeLast(4, [ 'foo', 'bar', 'baz' ])).toEqual([
    'foo',
    'bar',
    'baz',
  ])

  expect(takeLast(10, [ 'foo', 'bar', 'baz' ])).toEqual([
    'foo',
    'bar',
    'baz',
  ])
})

test('with strings', () => {
  expect(takeLast(3, 'rambda')).toEqual('bda')

  expect(takeLast(7, 'rambda')).toEqual('rambda')
})

test('with negative index', () => {
  expect(takeLast(-1, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
  expect(takeLast(-Infinity, [ 1, 2, 3 ])).toEqual([ 1, 2, 3 ])
})
R.takeLast source
import baseSlice from './internal/baseSlice'

export function takeLast(n, list){
  if (arguments.length === 1) return _list => takeLast(n, _list)

  const len = list.length
  if (n < 0) return list.slice()
  let numValue = n > len ? len : n

  if (typeof list === 'string') return list.slice(len - numValue)

  numValue = len - numValue

  return baseSlice(list, numValue, len)
}

Try in REPL


tap

tap(fn: Function, input: T): T

  • It applies function to input and pass the input back. Use case is debuging in the middle of R.compose.
let a = 1
const sayX = x => (a = x)

const result = R.tap(sayX, 100)
// both `a` and `result` are `100`
R.tap tests
import { tap } from './tap'

test('tap', () => {
  let a = 1
  const sayX = x => a = x

  expect(tap(sayX, 100)).toEqual(100)
  expect(tap(sayX)(100)).toEqual(100)
  expect(a).toEqual(100)
})
R.tap source
export function tap(fn, x){
  if (arguments.length === 1) return _x => tap(fn, _x)

  fn(x)

  return x
}

Try in REPL


test

test(regExpression: Regex, str: string): boolean

  • Determines whether str matches regExpression
R.test(/^f/, 'foo')
// => true

Source

Try in REPL


times

times(fn: Function, n: number): T[]

It returns the result of applying function fn over members of range array. The range array includes numbers between 0 and n(exclusive).

R.times(R.identity, 5)
//=> [0, 1, 2, 3, 4]
R.times tests
import { times } from './times'
import { identity } from './identity'
import assert from 'assert'

test('happy', () => {
  const result = times(identity, 5)

  expect(result).toEqual([ 0, 1, 2, 3, 4 ])
})

test('with bad input', () => {
  assert.throws(() => { times(3)('cheers!') }, RangeError)
  assert.throws(() => { times(identity, -1) }, RangeError)
})

test('curry', () => {
  const result = times(identity)(5)

  expect(result).toEqual([ 0, 1, 2, 3, 4 ])
})
R.times source
import { map } from './map'
import { range } from './range'

export function times(fn, n){
  if (arguments.length === 1) return _n => times(fn, _n)
  if (!Number.isInteger(n) || n < 0) throw new RangeError('n must be an integer')

  return map(fn, range(0, n))
}

Try in REPL


toLower

toLower(str: string): string

R.toLower('FOO') // => 'foo'
R.toLower tests
import { toLower } from './toLower'

test('toLower', () => {
  expect(toLower('FOO|BAR|BAZ')).toEqual('foo|bar|baz')
})
R.toLower source
export function toLower(str){
  return str.toLowerCase()
}

Try in REPL


toPairs

toPairs(obj: object): any[]

It transforms an object to a list.

const list = {
  a : 1,
  b : 2,
  c : [ 3, 4 ],
}
const expected = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ]

const result = R.toPairs(list)
// expected === result
R.toPairs tests
import { toPairs } from './toPairs'

const obj = {
  a : 1,
  b : 2,
  c : [ 3, 4 ],
}
const expected = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', [ 3, 4 ] ] ]

test('', () => {
  expect(toPairs(obj)).toEqual(expected)
})
R.toPairs source
export function toPairs(obj){
  return Object.entries(obj)
}

Try in REPL


toString

toString(x: any): string

R.toString([1, 2]) // => '1,2'
R.toString tests
import { toString } from './toString'

test('', () => {
  expect(toString([ 1, 2, 3 ])).toEqual('1,2,3')
})
R.toString source
export function toString(val){
  return val.toString()
}

Try in REPL


toUpper

toUpper(str: string): string

R.toUpper('foo') // => 'FOO'
R.toUpper tests
import { compose } from './compose'
import { join } from './join'
import { map } from './map'
import { split } from './split'
import { toUpper } from './toUpper'

test('toUpper', () => {
  expect(
    compose(
      join(''),
      map(toUpper),
      split('')
    )('foo|bar|baz')
  ).toEqual('FOO|BAR|BAZ')
})
R.toUpper source
export function toUpper(str){
  return str.toUpperCase()
}

Try in REPL


trim

trim(str: string): string

R.trim('  foo  ') // => 'foo'
R.trim tests
import { trim } from './trim'
test('trim', () => {
  expect(trim(' foo ')).toEqual('foo')
})
R.trim source
export function trim(str){
  return str.trim()
}

Try in REPL


type

type(a: any): string

R.type(() => {}) // => 'Function'
R.type(async () => {}) // => 'Async'
R.type([]) // => 'Array'
R.type({}) // => 'Object'
R.type('foo') // => 'String'
R.type(1) // => 'Number'
R.type(true) // => 'Boolean'
R.type(null) // => 'Null'
R.type(/[A-z]/) // => 'RegExp'

const delay = ms => new Promise(resolve => {
  setTimeout(function () {
    resolve()
  }, ms)
})
R.type(delay) // => 'Promise'
R.type tests
import { type } from './type'
import { type as ramdaType } from 'ramda'

test('with simple promise', () => {
  expect(type(Promise.resolve(1))).toBe('Promise')
})

test('with new Boolean', () => {
  expect(type(new Boolean(true))).toBe('Boolean')
})

test('with new String', () => {
  expect(type(new String('I am a String object'))).toEqual('String')
})

test('with new Number', () => {
  expect(type(new Number(1))).toBe('Number')
})

test('with new promise', () => {
  const delay = ms =>
    new Promise(resolve => {
      setTimeout(() => {
        resolve(ms + 110)
      }, ms)
    })

  expect(type(delay(10))).toEqual('Promise')
})

test('async function', () => {
  expect(type(async () => {})).toEqual('Async')
})

test('async arrow', () => {
  const asyncArrow = async () => {}
  expect(type(asyncArrow)).toBe('Async')
})

test('function', () => {
  const fn1 = () => {}
  const fn2 = function(){}

  function fn3(){}

  [ () => {}, fn1, fn2, fn3 ].map(val => {
    expect(type(val)).toEqual('Function')
  })
})

test('object', () => {
  expect(type({})).toEqual('Object')
})

test('number', () => {
  expect(type(1)).toEqual('Number')
})

test('boolean', () => {
  expect(type(false)).toEqual('Boolean')
})

test('string', () => {
  expect(type('foo')).toEqual('String')
})

test('null', () => {
  expect(type(null)).toEqual('Null')
})

test('array', () => {
  expect(type([])).toEqual('Array')
  expect(type([ 1, 2, 3 ])).toEqual('Array')
})

test('regex', () => {
  expect(type(/\s/g)).toEqual('RegExp')
})

test('undefined', () => {
  expect(type(undefined)).toEqual('Undefined')
})

test('not a number', () => {
  expect(type(Number('s'))).toBe('NaN')
})

test('function inside object 1', () => {
  const obj = {
    f(){
      return 4
    },
  }

  expect(type(obj.f)).toBe('Function')
  expect(ramdaType(obj.f)).toBe('Function')
})

test('function inside object 2', () => {
  const name = 'f'
  const obj = {
    [ name ](){
      return 4
    },
  }
  expect(type(obj.f)).toBe('Function')
  expect(ramdaType(obj.f)).toBe('Function')
})
R.type source
export function type(input){
  const typeOf = typeof input
  const asStr = input && input.toString ? input.toString() : ''

  if (input === null){
    return 'Null'
  } else if (input === undefined){
    return 'Undefined'
  } else if (typeOf === 'boolean'){
    return 'Boolean'
  } else if (typeOf === 'number'){
    return Number.isNaN(input) ? 'NaN' : 'Number'
  } else if (typeOf === 'string'){
    return 'String'
  } else if (Array.isArray(input)){
    return 'Array'
  } else if (input instanceof RegExp){
    return 'RegExp'
  }

  if ([ 'true', 'false' ].includes(asStr)) return 'Boolean'
  if (!Number.isNaN(Number(asStr))) return 'Number'
  if (asStr.startsWith('async')) return 'Async'
  if (asStr === '[object Promise]') return 'Promise'
  if (typeOf === 'function') return 'Function'
  if (input instanceof String) return 'String'

  return 'Object'
}

Try in REPL


uniq

uniq(arr: T[]): T[]

It returns a new array containing only one copy of each element in arr.

R.uniq([1, 1, 2, 1])
// => [1, 2]
R.uniq tests
import { uniq } from './uniq'

test('uniq', () => {
  expect(uniq([ 1, 2, 3, 3, 3, 1, 2, 0 ])).toEqual([ 1, 2, 3, 0 ])
  expect(uniq([ 1, 1, 2, 1 ])).toEqual([ 1, 2 ])
  expect([ 1, '1' ]).toEqual([ 1, '1' ])
  expect(uniq([ [ 42 ], [ 42 ] ])).toEqual([ [ 42 ] ])
})
R.uniq source
import { includes } from './includes'

export function uniq(list){
  let index = -1
  const willReturn = []

  while (++index < list.length){
    const value = list[ index ]

    if (!includes(value, willReturn)){
      willReturn.push(value)
    }
  }

  return willReturn
}

Try in REPL


uniqWith

uniqWith(fn: Function, arr: T[]): T[]

It returns a new array containing only one copy of each element in arr according to boolean returning function fn.

const arr = [
  {id: 0, title:'foo'},
  {id: 1, title:'bar'},
  {id: 2, title:'baz'},
  {id: 3, title:'foo'},
  {id: 4, title:'bar'},
]

const expectedResult = [
  {id: 0, title:'foo'},
  {id: 1, title:'bar'},
  {id: 2, title:'baz'},
]

const fn = (x,y) => x.title === y.title

const result = R.uniqWith(fn, arr)

console.log(R.equals(result, expectedResult)) // => true
R.uniqWith tests
import { uniqWith } from './uniqWith'

test('', () => {
  const input = [
    {
      id    : 0,
      title : 'foo',
    },
    {
      id    : 1,
      title : 'bar',
    },
    {
      id    : 2,
      title : 'baz',
    },
    {
      id    : 3,
      title : 'foo',
    },
    {
      id    : 4,
      title : 'bar',
    },
  ]

  const expectedResult = [
    {
      id    : 0,
      title : 'foo',
    },
    {
      id    : 1,
      title : 'bar',
    },
    {
      id    : 2,
      title : 'baz',
    },
  ]

  const fn = (x, y) => x.title === y.title

  const result = uniqWith(fn, input)
  const curriedResult = uniqWith(fn)(input)

  expect(result).toEqual(expectedResult)

  expect(curriedResult).toEqual(expectedResult)
})

test('uniqWith', () => {
  const input = [
    {
      id    : 0,
      title : 'foo',
    },
    {
      id    : 1,
      title : 'bar',
    },
    {
      id    : 2,
      title : 'baz',
    },
    {
      id    : 3,
      title : 'foo',
    },
    {
      id    : 4,
      title : 'bar',
    },
  ]

  const expectedResult = [
    {
      id    : 0,
      title : 'foo',
    },
    {
      id    : 1,
      title : 'bar',
    },
    {
      id    : 2,
      title : 'baz',
    },
  ]

  const fn = (x, y) => x.title === y.title

  const result = uniqWith(fn, input)
  //const result = uniqWith(Ramda.eqBy(Ramda.prop('title')), input)

  expect(result).toEqual(expectedResult)
})
R.uniqWith source
import { any } from './any'

export function uniqWith(fn, list){
  if (arguments.length === 1) return _list => uniqWith(fn, _list)

  let index = -1
  const len = list.length
  const willReturn = []

  while (++index < len){
    const value = list[ index ]
    const flag = any(
      willReturnInstance => fn(value, willReturnInstance),
      willReturn
    )

    if (!flag){
      willReturn.push(value)
    }
  }

  return willReturn
}

Try in REPL


update

update(i: number, replaceValue: T, arr: T[]): T[]

It returns a new copy of the arr with the element at i index replaced with replaceValue.

R.update(0, 'foo', ['bar', 'baz'])
// => ['foo', baz]
R.update tests
import { update } from './update'

test('update', () => {
  expect(update(1)(0)([ 1, 2, 3 ])).toEqual([ 1, 0, 3 ])
  expect(update(1, 11, [ 0, 1, 2 ])).toEqual([ 0, 11, 2 ])
})
R.update source
export function update(idx, val, list){
  if (val === undefined){
    return (_val, _list) => update(idx, _val, _list)
  } else if (list === undefined){
    return _list => update(idx, val, _list)
  }

  const arrClone = list.slice()

  return arrClone.fill(val, idx, idx + 1)
}

Try in REPL


values

values(obj: Object): Array

It returns array with of all values in obj.

R.values({a: 1, b: 2})
// => [1, 2]
R.values tests
import { values } from './values'

test('happy', () => {
  expect(
    values({
      a : 1,
      b : 2,
      c : 3,
    })
  ).toEqual([ 1, 2, 3 ])
})

test('with bad input', () => {
  expect(values(null)).toEqual([])
  expect(values(undefined)).toEqual([])
  expect(values(55)).toEqual([])
  expect(values('foo')).toEqual([])
  expect(values(true)).toEqual([])
  expect(values(false)).toEqual([])
  expect(values(NaN)).toEqual([])
  expect(values(Infinity)).toEqual([])
  expect(values([])).toEqual([])
})
R.values source
import { type } from './type.js'

export function values(obj){
  if (type(obj) !== 'Object') return []

  return Object.values(obj)
}

Try in REPL


without

without(a: T[], b: T[]): T[]

It will return a new array based on b array.

This array contains all members of b array, that doesn't exist in a array.

Method R.equals is used to determine the existance of b members in a array.

R.without([1, 2], [1, 2, 3, 4])
// => [3, 4]
R.without tests
import { without } from './without'

test('should return a new list without values in the first argument ', () => {
  const itemsToOmit = [ 'A', 'B', 'C' ]
  const collection = [ 'A', 'B', 'C', 'D', 'E', 'F' ]

  expect(without(itemsToOmit, collection)).toEqual([ 'D', 'E', 'F' ])
  expect(without(itemsToOmit)(collection)).toEqual([ 'D', 'E', 'F' ])
})

test('ramda test', () => {
  expect(without([ 1, 2 ])([ 1, 2, 1, 3, 4 ])).toEqual([ 3, 4 ])
})
R.without source
import { includes } from './includes'
import { reduce } from './reduce'

export function without(left, right){
  if (right === undefined){
    return _right => without(left, _right)
  }

  return reduce(
    (accum, item) =>
      includes(item, left) ? accum : accum.concat(item),
    [],
    right
  )
}

Try in REPL


zip

zip(a: K[], b: V[]): Array

It will return a new array containing tuples of equally positions items from both lists. The returned list will be truncated to match the length of the shortest supplied list.

R.zip([1, 2], ['A', 'B'])
// => [[1, 'A'], [2, 'B']]

// truncates to shortest list
R.zip([1, 2, 3, 4], ['A', 'B'])
// => [[1, 'A'], [2, 'B']]
R.zip tests
import { zip } from './zip'

const array1 = [ 1, 2, 3 ]
const array2 = [ 'A', 'B', 'C' ]

test('should return an array', () => {
  const actual = zip(array1)(array2)
  expect(actual).toBeInstanceOf(Array)
})

test('should return and array or tuples', () => {
  const expected = [ [ 1, 'A' ], [ 2, 'B' ], [ 3, 'C' ] ]
  const actual = zip(array1, array2)
  expect(actual).toEqual(expected)
})

test('should truncate result to length of shorted input list', () => {
  const expectedA = [ [ 1, 'A' ], [ 2, 'B' ] ]
  const actualA = zip([ 1, 2 ], array2)
  expect(actualA).toEqual(expectedA)

  const expectedB = [ [ 1, 'A' ], [ 2, 'B' ] ]
  const actualB = zip(array1, [ 'A', 'B' ])
  expect(actualB).toEqual(expectedB)
})
R.zip source
export function zip(left, right){
  if (arguments.length === 1) return _right => zip(left, _right)

  const result = []
  const length = Math.min(left.length, right.length)

  for (let i = 0; i < length; i++){
    result[ i ] = [ left[ i ], right[ i ] ]
  }

  return result
}

Try in REPL


zipObj

zipObj(a: K[], b: V[]): Object

It will return a new object with keys of a array and values of b array.

R.zipObj(['a', 'b', 'c'], [1, 2, 3])
//=> {a: 1, b: 2, c: 3}

// truncates to shortest list
R.zipObj(['a', 'b', 'c'], [1, 2])
//=> {a: 1, b: 2}
R.zipObj tests
import { zipObj } from './zipObj'
import { equals } from './equals.js'

test('zipObj', () => {
  expect(zipObj([ 'a', 'b', 'c' ], [ 1, 2, 3 ])).toEqual({
    a : 1,
    b : 2,
    c : 3,
  })
})

test('0', () => {
  expect(zipObj([ 'a', 'b' ])([ 1, 2, 3 ])).toEqual({
    a : 1,
    b : 2,
  })
})

test('1', () => {
  expect(zipObj([ 'a', 'b', 'c' ])([ 1, 2 ])).toEqual({
    a : 1,
    b : 2,
  })
})

test('ignore extra keys', () => {
  const result = zipObj([ 'a', 'b', 'c', 'd', 'e', 'f' ], [ 1, 2, 3 ])
  const expected = {
    a : 1,
    b : 2,
    c : 3,
  }

  expect(
    equals(result, expected)
  ).toBeTruthy()
})
R.zipObj source
import { take } from './take'
export function zipObj(keys, values){
  if (arguments.length === 1) return yHolder => zipObj(keys, yHolder)

  return take(values.length, keys).reduce((prev, xInstance, i) => {
    prev[ xInstance ] = values[ i ]

    return prev
  }, {})
}

Try in REPL


---

Use with ES5

import omit from 'rambda/lib/omit'

Latest version that has this feature is 2.3.1

Changelog

  • 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.3 Doesn't exist because NPM is great at handling errors.

  • 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

Additional info

Running benchmarks

  • To run all benchmarks

yarn benchmark

Projects using Rambda

Projects using Rambdax

Rambda references

Links to Rambda