Rambda
Faster alternative to Ramda
in just 12kB - Documentation
Argumentation
I admire Ramda
, as it is great library in what it does. My main problem was its size. Even custom builds didn't deliver satisfactory results. Also I already had Ramda
habits and I didn't want to switch to Lodash
.
Then I realized that my best solution was to publish a library that recreates the functionality of some Ramda
methods with less code.
Rambda partially shadows Ramda's API, so you need to check in Rambda's documentation, if the methods you need are available.
Generally speaking, if you have never used methods such as R.transduce
, Rambda API should be enough for your needs.
Example use
const R = require('rambda')
const result = R.compose(
R.filter( R.equals( 2 ) ),
R.map( R.add( 1 ) )
)({ a: 1, b: 2, c: 3 })
console.log(result) // => '{a: 2}'
Install
https://cdnjs.cloudflare.com/ajax/libs/rambda/0.9.1/webVersion.js
Differences between Rambda and Ramda
-
Rambda's type detect async functions and unresolved Promises
. The returned values are 'Async'
and 'Promise'
.
-
Rambda's equals doesn't protect against circular structures as Ramda.equals does.
-
Rambda's path, pick and omit accepts both string and array as condition argument('x.y.z' == ['x','y','z']).
-
Rambda's partialCurry, typedDefaultTo, typedPathOr, includes, padStart and padEnd are not part of Ramda API.
-
Rambda's reverse modifies the array, instead of returning reversed copy of it.
If you need more Ramda methods in Rambda, you may either submit a PR
or check the extended version of Rambda - Rambdax
API
add
add(a: Number, b: Number): Number
R.add(2, 3)
addIndex
addIndex(fn: Function): Function
const mapWithIndex = R.addIndex(R.map)
mapWithIndex(
(val, index) => `${val} - ${index}`,
["A", "B", "C"]
)
adjust
adjust(replaceFn: Function, i:Number, arr:Array): Array
It replaces i
index in arr
with the result of replaceFn(arr[i])
.
R.adjust(a => a + 1, 0, [0, 100])
all
all(fn: Function, arr: Array): 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
R.all(fn, arr) // => true
allPass
allPass(rules: Array, 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,
]
R.allPass(conditionArr, obj) // => true
always
always(x: any): Function
It returns function that always returns x
.
const fn = R.always(7)
fn()// => 7
fn()// => 7
any
any(condition: Function, arr: Array): 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])
R.any(a => a * a > 10)([1, 2, 3])
append
append(valueToAppend: any, arr: Array): Array
R.append('foo', ['bar', 'baz'])
both
both(x: Function, y: Function, input: any): Boolean
It returns true
if both function x
and function y
return true
, when input
is their argument.
const fn = R.both(
a => a > 10,
a => a < 20
)
fn(15) //=> true
fn(30) //=> false
compose
compose(fn1: Function, ... , fnN: Function): any
It performs right-to-left function composition.
const result = R.compose(
R.map(x => x * 2)
R.filter(x => x > 2),
)([1, 2, 3, 4])
console.log(result) // => [6, 8]
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)
.
R.complement(R.always(0)) // => true
R.complement(R.always(true)) // => false
concat
concat(x: Array|String, y: Array|String): Array|String
It returns new string or array, which is result merging x
and y
.
R.concat([1, 2])([3, 4]) // => [1, 2, 3, 4]
R.concat('foo', 'bar') // => 'foobar'
contains
contains(valueToFind: any, arr: Array): Boolean
It returns true if valueToFind
is part of arr
.
R.contains(2, [1, 2])
R.contains(3, [1, 2])
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)
g(4)
defaultTo
defaultTo(defaultValue: T, inputArgument: any): T
It returns defaultValue
, if inputArgument
is undefined
, null
or NaN
.
It returns inputArgument
in any other case.
R.defaultTo('foo', undefined)
R.defaultTo('foo', 'bar')
R.defaultTo('foo', 1)
divide
R.divide(71, 100)
drop
drop(howManyToDrop: Number, arrOrStr: Array|String): Array|String
It returns arrOrStr
with howManyToDrop
items dropped from the left.
R.drop(1, ['foo', 'bar', 'baz'])
R.drop(1, 'foo')
dropLast
dropLast(howManyToDrop: Number, arrOrStr: Array|String): Array|String
It returns arrOrStr
with howManyToDrop
items dropped from the right.
R.dropLast(1, ['foo', 'bar', 'baz'])
R.dropLast(1, 'foo')
endsWith
endsWith(x: any, arrOrStr: Array|String): Boolean
R.endsWith(
'bar',
"foo-bar"
) // => true
R.endsWith(
'baz',
"foo-bar"
) // => false
either
const fn = R.either(
a => a > 10,
a => a % 2 === 0
)
fn(15) //=> true
fn(6) //=> true
fn(7) //=> false
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, 1)
R.equals({}, {})
R.equals([1, 2, 3], [1, 2, 3])
F
R.F() // => false
filter
filter(filterFn: Function, arr: Array): Array
Filters arr
throw boolean returning filterFn
const filterFn = a => a % 2 === 0
R.filter(filterFn, [1, 2, 3, 4])
find
find(findFn: Function, arr: Array): 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}]
R.find(findFn, arr)
findIndex
findIndex(findFn: Function, arr: Array): 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}]
R.find(findFn, arr)
flatten
flatten(arr: Array): Array
R.flatten([ 1, [ 2, [ 3 ] ] ])
flip
flip(fn: Function): Function
It returns function which calls fn
with exchanged first and second argument.
const subtractFlip = R.flip(R.subtract)
R.subtractFlip(1,7)
Note that it works only for functions that expects two arguments. If third argument is passed, then undefined
is returned.
has
has(prop: String, obj: Object): Boolean
- It returns
true
if obj
has property prop
.
R.has("a", {a: 1})
R.has("b", {a: 1})
head
head(arrOrStr: Array|String): any
It returns the first element of arrOrStr
.
R.head([1, 2, 3])
R.head('foo')
identity
identity(x: T): T
It just passes back the supplied arguments.
R.identity(7) // => 7
ifElse
ifElse(condition: Function, ifFn: Function, elseFn: Function): Function
It returns function, which expect input
as argument and returns finalResult
.
When the 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
)
fn(8) // => 80
fn(11) // => 22
indexOf
indexOf(valueToFind: any, arr: Array): Number
It returns -1
or the index of the first element of arr
equal of valueToFind
.
R.indexOf(1, [1, 2])
init
init(arrOrStr: Array|String): Array|String
- It returns all but the last element of
arrOrStr
.
R.init([1, 2, 3])
R.init('foo')
join
join(separator: String, arr: Array): String
R.join('-', [1, 2, 3])
isNil
isNil(x: any): Boolean
It returns true
is x
is either null
or undefined
.
R.isNil(null)
R.isNil(1)
last
last(arrOrStr: Array|String): any
- It returns the last element of
arrOrStr
.
R.last(['foo', 'bar', 'baz'])
R.last('foo')
lastIndexOf
lastIndexOf(x: any, arr: Array): Number
R.lastIndexOf(1, [1, 2, 3, 1, 2]) // => 3
R.lastIndexOf(10, [1, 2, 3, 1, 2]) // => -1
length
length(arrOrStr: Array|String): Number
R.length([1, 2, 3])
map
map(mapFn: Function, arr: Array): Array
It returns the result of looping through arr
with mapFn
.
const mapFn = x => x * 2;
R.map(mapFn, [1, 2, 3])
match
match(regExpression: Regex, str: String): Array
R.match(/([a-z]a)/g, 'bananas')
merge
merge(a: Object, b: Object)
It returns result of Object.assign({}, a, b)
.
R.merge({ 'foo': 0, 'bar': 1 }, { 'foo': 7 })
modulo
modulo(a: Number, b: Number): Number
It returns the remainder of operation a/b
.
R.module(14,3)
multiply
multiply(a: Number, b: Number): Number
It returns the result of operation a*b
.
R.module(14,3)
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
omit
omit(propsToOmit: Array, obj: Object): Object
It returns a partial copy of an obj
with omitting propsToOmit
R.omit(['a', 'd'], {a: 1, b: 2, c: 3})
path
path(pathToSearch: Array|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}})
R.path(['a', 'b'], {a: {b: 2}})
R.path(['a', 'c'], {a: {b: 2}})
pathOr
pathOr(defaultValue: any, pathToSearch: Array|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}})
R.pathOr(1, ['a', 'b'], {a: {b: 2}})
R.pathOr(1, ['a', 'c'], {a: {b: 2}})
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})
curried({b: 3, c: 10})
pick
pick(propsToPick: Array, 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}
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])
console.log(result) // => [6, 8]
pluck
pluck(property: String, arr: Array): Array
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]
prepend
prepend(x: any, arr: Array): Array
It adds x
to the start of the array arr
.
R.prepend('foo', ['bar', 'baz'])
prop
prop(propToFind: String, obj: Object): any
It returns undefined
or the value of property propToFind
in obj
R.prop('x', {x: 100})
R.prop('x', {a: 1})
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
R.propEq(propToFind, valueToMatch)({foo: 0})
R.propEq(propToFind, valueToMatch)({foo: 1})
range
range(start: Number, end: Number): Array
It returns a array of numbers from start
(inclusive) to end
(exclusive).
R.range(0, 2)
reduce
reduce(iteratorFn: Function, accumulator: any, array: Array): any
It returns a single item by iterating through the list, successively calling the iterator function iteratorFn
and passing it an accumulator
value and the current value from the array, and then passing the result to the next call.
The iterator function behaves like the native callback of the Array.prototype.reduce
method.
const iteratorFn = (acc, val) => acc + val
R.reduce(iteratorFn, 1, [1, 2, 3])
repeat
repeat(valueToRepeat: T, num: Number): Array
R.repeat('foo', 2)
replace
replace(strOrRegex: String|Regex, replacer: String, str: String): String
Replace strOrRegex
found in str
with replacer
R.replace('foo', 'bar', 'foo foo')
R.replace(/foo/, 'bar', 'foo foo')
R.replace(/foo/g, 'bar', 'foo foo')
reverse
!!! It modifies the array instead of returning new copy, as original Ramda
method does.
const arr = [1, 2]
R.reverse(arr)
console.log(arr) // => [2, 1]
sort
sort(sortFn: Function, arr: Array): Array
It returns copy of arr
sorted by sortFn
.
sortFn
must return Number
const sortFn = (a, b) => a - b
R.sort(sortFn, [3, 1, 2])
sortBy
sortBy(sortFn: Function, arr: Array): Array
It returns copy of arr
sorted by sortFn
.
sortFn
must return value for comparison
const sortFn = obj => obj.foo
R.sortBy(sortFn, [
{foo: 1},
{foo: 0}
])
split
split(separator: String, str: String): Array
R.split('-', 'a-b-c')
splitEvery
splitEvery(sliceLength: Number, arrOrString: Array|String): Array
- Splits
arrOrStr
into slices of sliceLength
R.splitEvery(2, [1, 2, 3])
R.splitEvery(3, 'foobar')
startsWith
startsWith(x: any, arrOrStr: Array|String): Boolean
R.endsWith(
'bar',
"foo-bar"
) // => true
R.endsWith(
'baz',
"foo-bar"
) // => false
subtract
subtract(a: Number, b: Number): Number
R.subtract(3, 1)
T
R.T() // => true
tail
tail(arrOrStr: Array|String): Array|String
- It returns all but the first element of
arrOrStr
R.tail([1, 2, 3])
R.tail('foo')
take
take(num: Number, arrOrStr: Array|String): Array|String
- It returns the first
num
elements of arrOrStr
.
R.take(1, ['foo', 'bar'])
R.take(2, ['foo'])
takeLast
takeLast(num: Number, arrOrStr: Array|String): Array|String
- It returns the last
num
elements of arrOrStr
.
R.takeLast(1, ['foo', 'bar'])
R.takeLast(2, ['foo'])
test
test(regExpression: Regex, str: String): Boolean
- Determines whether
str
matches regExpression
R.test(/^f/, 'foo')
R.test(/^f/, 'bar')
toLower
toLower(str: String): String
R.toLower('FOO')
toString
toString(x: any): String
R.toString([1, 2]) // => '1,2'
toUpper
toUpper(str: String): String
R.toUpper('foo')
trim
trim(str: String): String
R.trim(' foo ')
type
type(a: any): String
R.type(() => {})
R.type(async () => {})
R.type([])
R.type({})
R.type('foo')
R.type(1)
R.type(true)
R.type(null)
R.type(/[A-z]/)
const delay = ms => new Promise(resolve => {
setTimeout(function () {
resolve()
}, ms)
})
R.type(delay)
typedDefaultTo
typedDefaultTo(defaultValue: T, inputArgument: any): T
It returns defaultValue
, if inputArgument
and defaultValue
has different types.
It returns inputArgument
in any other case.
R.typedDefaultTo('foo', undefined)
R.typedDefaultTo('foo', 'bar')
R.typedDefaultTo('foo', 1)
- Note that
typedDefaultTo
is method specific for Rambda and the method is not part of Ramda's API
typedPathOr
typedPathOr(defaultValue: any, pathToSearch: Array|String, obj: Object): any
pathFound
is the result of calling R.path(pathToSearch, obj)
.
If pathFound
has different type than defaultValue
, then defaultValue
will be returned.
If pathFound
has the same type as defaultValue
, then pathFound
will be returned.
R.typedPathOr(1, 'a.b', {a: {b: 2}})
R.typedPathOr(1, 'a.b', {a: {b: 'foo'}})
- Note that
typedPathOr
is method specific for Rambda and the method is not part of Ramda's API
uniq
uniq(arr: Array): Array
It returns a new array containing only one copy of each element in arr
.
R.uniq([1, 1, 2, 1])
R.uniq([1, '1'])
update
update(i: Number, replaceValue: any, arr: Array): Array
It returns a new copy of the arr
with the element at i
index
replaced with replaceValue
.
R.update(0, "foo", ['bar', 'baz'])
values
values(obj: Object): Array
It returns array with of all values in obj
.
R.values({a: 1, b: 2})
includes
includes(x: any, arrOrStr: Array|String): Boolean
R.includes(1, [1, 2]) // => true
R.includes('oo', 'foo') // => true
R.includes('z', 'foo') // => false
Note that this method is not part of Ramda
API.
padEnd
padEnd(x: Number, str: String): String
R.padEnd(3, 'foo') // => 'foo '
Note that this method is not part of Ramda
API.
padStart
padStart(x: Number, str: String): String
R.padStart(3, 'foo') // => ' foo'
Note that this method is not part of Ramda
API.
Benchmark
Flowtype
I haven't tested it fully, but the partial test shows that Ramda
definitions can be used.
You need to replace declare module ramda
with declare module rambda
on line 10 and store the file as rambda.js
in your flow-typed folder
Changelog
- 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
npm test
before npm 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
Browse by category
Function
addIndex
always
compose
curry
F
identity
T
tap
Math
add
divide
modulo
multiply
subtract
List
adjust
all
any
append
concat
contains
drop
dropLast
endsWith
filter
find
findIndex
flatten
head
indexOf
init
join
last
lastIndexOf
length
map
pluck
prepend
range
reduce
repeat
reverse
sort
splitEvery
startsWith
tail
take
takeLast
uniq
update
Logic
allPass
both
complement
defaultTo
either
ifElse
not
Object
has
merge
omit
path
pick
prop
values
Relation
equals
propEq
sortBy
String
match
replace
split
test
toLower
toString
toUpper
trim
Contribution guidelines
If you want to add another Ramda
method to the API, please feel free to submit a PR
.
The only requirement is the new method to have exact or very close implementation compared to the corresponding Ramda
method.
I give you example steps of the PR
process.
Create a method file in modules
folder.
If the new method is R.endsWith
, then the created file will be ./modules/endsWith.js
Write the function declaration and function's logic.
function endsWith(x, arrOrStr){
return arrOrStr.endsWith(x)
}
Any method, which takes more than one argument, should be curried.
We can use the standard curring used throughout Rambda
.
function endsWith(x, arrOrStr){
if(arrOrStr === undefined){
return arrOrStrHolder => endsWith(x, arrOrStrHolder)
}
return arrOrStr.endsWith(x)
}
module.exports = endsWith
Or we can also use R.curry
, but it is not as performant as the example above.
const curry = require('./curry')
function endsWith(x, arrOrStr){
if(arrOrStr === undefined){
return holder => endsWith(x, arrOrStr)
}
return arrOrStr.endsWith(x)
}
module.exports = curry(endsWith)
Edit rambda.js
file
Exported methods are sorted alphabetically
exports.dropLast = require("./modules/dropLast")
exports.endsWith = require("./modules/endsWith")
exports.equals = require("./modules/equals")
Write your test cases
Create file endsWith.js
in folder __tests__
const R = require('../rambda')
test('endsWith', () => {
expect(R.endsWith('oo')('foo')).toBeTruthy()
})
Run npm test
to validate your tests
Edit ./README.md
to add documentation
Note that your documentation should match the pattern visible across ./README.md
Lint your files
npm run lint modules/endsWith.js
npm run lint __tests__/endsWith.js
Submit PR
Expect response within 2 days.
Additional info
Projects using Rambda
Articles about Rambda