Security News
RubyGems.org Adds New Maintainer Role
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Rambda is a lightweight and fast utility library that provides a variety of functions for functional programming in JavaScript. It is a smaller and faster alternative to Ramda, offering a similar API but with a focus on performance and simplicity.
Currying
Currying is a technique of evaluating functions with multiple arguments, one at a time. Rambda's `curry` function allows you to transform a function so that it can be called with fewer arguments than it expects, returning a new function that takes the remaining arguments.
const R = require('rambda');
const add = R.curry((a, b) => a + b);
const add5 = add(5);
console.log(add5(3)); // 8
Composition
Function composition is the process of combining two or more functions to produce a new function. Rambda's `compose` function allows you to create a pipeline of functions that are executed from right to left.
const R = require('rambda');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const addAndMultiply = R.compose(R.multiply(2), R.add(3));
console.log(addAndMultiply(4)); // 14
Cloning
Cloning is the process of creating a deep copy of an object. Rambda's `clone` function allows you to create a deep copy of an object, ensuring that changes to the new object do not affect the original object.
const R = require('rambda');
const obj = {a: 1, b: 2};
const clonedObj = R.clone(obj);
console.log(clonedObj); // {a: 1, b: 2}
Filtering
Filtering is the process of selecting a subset of items from a collection based on a predicate function. Rambda's `filter` function allows you to filter elements in an array or object based on a provided predicate.
const R = require('rambda');
const isEven = n => n % 2 === 0;
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = R.filter(isEven, numbers);
console.log(evenNumbers); // [2, 4, 6]
Mapping
Mapping is the process of transforming each item in a collection using a provided function. Rambda's `map` function allows you to apply a function to each element in an array or object, returning a new array or object with the transformed elements.
const R = require('rambda');
const double = n => n * 2;
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = R.map(double, numbers);
console.log(doubledNumbers); // [2, 4, 6, 8, 10]
Lodash is a popular utility library that provides a wide range of functions for common programming tasks. It is more feature-rich compared to Rambda but also larger in size. Lodash focuses on performance and ease of use, offering a comprehensive set of tools for working with arrays, objects, strings, and more.
Underscore is another utility library that provides a variety of functional programming helpers. It is similar to Lodash but with a smaller footprint and fewer features. Underscore offers a core set of functions for working with collections, arrays, objects, and functions, making it a good choice for projects that need a lightweight utility library.
Ramda is a functional programming library for JavaScript that emphasizes immutability and pure functions. It offers a similar API to Rambda but with a larger set of functions and a focus on functional programming principles. Ramda is more feature-rich but also larger in size compared to Rambda.
Faster alternative to Ramda
in just 10kB - Documentation
Currenly Rambda
is more tree-shakable than Ramda
as you can see in this tree-shaking example.
Rambda
is generally more performant than Ramda
as the benchmarks can prove that.
You can clone this repo and run yarn run benchmark all
to see for yourself.
For example with Rambda
you can call R.path('a.b', {a: {b: 1}})
instead of R.path(['a','b'], {a: {b: 1}})
In Rambda
you can use both types of expression.
This is not a major change, but some developers would prefer to use 'a.b.c'
over ['a', 'b', 'c']
.
Same logic is applied to R.omit
method.
R.pick
is similar, but the separator is ,
not .
Initial 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.
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}'
Use yarn add rambda for Webpack
and Node.js
usage
For UMD usage either use ./dist/rambda.umd.js
or the CDN link at
https://cdnjs.cloudflare.com/ajax/libs/rambda/1.0.0/webVersion.js
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 flip works only for functions expecting two arguments.
Rambda's partialCurry and includes are not part of Ramda API.
Rambda's startsWith/endsWith work only with strings, instead with array and strings.
If you need more Ramda methods in Rambda, you may either submit a
PR
or check the extended version of Rambda - Rambdax
add(a: Number, b: Number): Number
R.add(2, 3) // => 5
addIndex(fn: Function): Function
const mapWithIndex = R.addIndex(R.map)
mapWithIndex(
(val, index) => `${val} - ${index}`,
['A', 'B', 'C']
) // => ['A - 0', 'B - 1', 'C - 2']
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]) // => [1, 100]
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(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(rules, obj) // => true
always(x: any): Function
It returns function that always returns x
.
const fn = R.always(7)
fn()// => 7
fn()// => 7
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]) // => true
R.any(a => a * a > 10)([1, 2, 3]) // => false
append(valueToAppend: any, arr: Array): Array
R.append('foo', ['bar', 'baz']) // => ['foo', 'bar', 'baz']
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(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(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(x: array|string, y: array|string): array|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'
contains(valueToFind: any, arr: Array): Boolean
It returns true if valueToFind
is part of arr
.
R.contains(2, [1, 2]) // => true
R.contains(3, [1, 2]) // => false
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) // => 10
dec(x: number): number
It decrements a number.
R.dec(2) // => 1
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) // => 'foo'
R.defaultTo('foo', 'bar') // => 'bar'
R.defaultTo('foo', 1) // => 1
R.divide(71, 100) // => 0.71
drop(howManyToDrop: Number, arrOrStr: Array|String): Array|String
It returns arrOrStr
with howManyToDrop
items dropped from the left.
R.drop(1, ['foo', 'bar', 'baz']) // => ['bar', 'baz']
R.drop(1, 'foo') // => 'oo'
dropLast(howManyToDrop: Number, arrOrStr: Array|String): Array|String
It returns arrOrStr
with howManyToDrop
items dropped from the right.
R.dropLast(1, ['foo', 'bar', 'baz']) // => ['foo', 'bar']
R.dropLast(1, 'foo') // => 'fo'
endsWith(x: String, str: String): Boolean
R.endsWith(
'bar',
'foo-bar'
) // => true
R.endsWith(
'foo',
"foo-bar"
) // => false
const fn = R.either(
a => a > 10,
a => a % 2 === 0
)
fn(15) //=> true
fn(6) //=> true
fn(7) //=> false
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) // => true
R.equals({}, {}) // => false
R.equals([1, 2, 3], [1, 2, 3]) // => true
R.F() // => false
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]) // => [2, 4]
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) // => {foo: 1}
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) // => 1
flatten(arr: Array): Array
R.flatten([ 1, [ 2, [ 3 ] ] ])
// => [ 1, 2, 3 ]
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)
// => 6
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]
Note, that unlike Ramda
's forEach, Rambda's one doesn't dispatch to forEach
method of arr
.
has(prop: String, obj: Object): Boolean
true
if obj
has property prop
.R.has("a", {a: 1}) // => true
R.has("b", {a: 1}) // => false
head(arrOrStr: Array|String): any
It returns the first element of arrOrStr
.
R.head([1, 2, 3]) // => 1
R.head('foo') // => 'f'
identity(x: T): T
It just passes back the supplied arguments.
R.identity(7) // => 7
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
)
fn(8) // => 80
fn(11) // => 22
inc(x: number): number
It increments a number.
R.inc(1) // => 2
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.
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]) // => 0
init(arrOrStr: Array|String): Array|String
arrOrStr
.R.init([1, 2, 3]) // => [1, 2]
R.init('foo') // => 'fo'
join(separator: String, arr: Array): String
R.join('-', [1, 2, 3]) // => '1-2-3'
isNil(xPrototype: any, x: any): boolean
It returns true
is x
is instance of xPrototype
.
R.is(String, 'foo') // => true
R.is(Array, 1) // => false
isNil(x: any): Boolean
It returns true
is x
is either null
or undefined
.
R.isNil(null) // => true
R.isNil(1) // => false
last(arrOrStr: Array|String): any
arrOrStr
.R.last(['foo', 'bar', 'baz']) // => 'baz'
R.last('foo') // => 'o'
lastIndexOf(x: any, arr: Array): 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
length(arrOrStr: Array|String): Number
R.length([1, 2, 3]) // => 3
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]) // => [2, 4, 6]
match(regExpression: Regex, str: String): Array
R.match(/([a-z]a)/g, 'bananas') // => ['ba', 'na', 'na']
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 }
modulo(a: Number, b: Number): Number
It returns the remainder of operation a/b
.
R.module(14,3) // => 2
multiply(a: Number, b: Number): Number
It returns the result of operation a*b
.
R.module(14,3) // => 2
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(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}) // => {b: 2, c: 3}
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}}) // => 1
R.path(['a', 'b'], {a: {b: 2}}) // => 2
R.path(['a', 'c'], {a: {b: 2}}) // => undefined
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}}) // => 2
R.pathOr(1, ['a', 'b'], {a: {b: 2}}) // => 2
R.pathOr(1, ['a', 'c'], {a: {b: 2}}) // => 1
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}) // => 16
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
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(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(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(x: any, arr: Array): Array
It adds x
to the start of the array arr
.
R.prepend('foo', ['bar', 'baz']) // => ['foo', 'bar', 'baz']
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
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}) // => true
R.propEq(propToFind, valueToMatch)({foo: 1}) // => false
range(start: Number, end: Number): Array
It returns a array of numbers from start
(inclusive) to end
(exclusive).
R.range(0, 2) // => [0, 1]
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]) // => 7
reject(fn: Function, arr: Array): Array
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
R.reject(fn, [1, 2, 3, 4]) // => [2, 4]
repeat(valueToRepeat: T, num: Number): Array
R.repeat('foo', 2) // => ['foo', 'foo']
replace(strOrRegex: String|Regex, replacer: String, str: String): String
Replace 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'
const arr = [1, 2]
R.reverse(arr)
console.log(arr) // => [2, 1]
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]) // => [1, 2, 3]
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}
])
// => [{foo: 0}, {foo: 1}]
split(separator: String, str: String): Array
R.split('-', 'a-b-c') // => ['a', 'b', 'c']
splitEvery(sliceLength: Number, arrOrString: Array|String): Array
arrOrStr
into slices of sliceLength
R.splitEvery(2, [1, 2, 3]) // => [[1, 2], [3]]
R.splitEvery(3, 'foobar') // => ['foo', 'bar']
startsWith(x: string, str: String): Boolean
R.startsWith(
'foo',
'foo-bar'
) // => true
R.startsWith(
'bar',
'foo-bar'
) // => false
subtract(a: Number, b: Number): Number
R.subtract(3, 1) // => 2
R.T() // => true
tail(arrOrStr: Array|String): Array|String
arrOrStr
R.tail([1, 2, 3]) // => [2, 3]
R.tail('foo') // => 'oo'
take(num: Number, arrOrStr: Array|String): Array|String
num
elements of arrOrStr
.R.take(1, ['foo', 'bar']) // => ['foo']
R.take(2, ['foo']) // => 'fo'
takeLast(num: Number, arrOrStr: Array|String): Array|String
num
elements of arrOrStr
.R.takeLast(1, ['foo', 'bar']) // => ['bar']
R.takeLast(2, ['foo']) // => 'oo'
test(regExpression: Regex, str: String): Boolean
str
matches regExpression
R.test(/^f/, 'foo') // => true
R.test(/^f/, 'bar') // => false
times(fn: Function, n: Number): Array
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]
toLower(str: String): String
R.toLower('FOO') // => 'foo'
toString(x: any): String
R.toString([1, 2]) // => '1,2'
toUpper(str: String): String
R.toUpper('foo') // => 'FOO'
trim(str: String): String
R.trim(' foo ') // => 'foo'
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"
uniq(arr: Array): Array
It returns a new array containing only one copy of each element in arr
.
R.uniq([1, 1, 2, 1]) // => [1, 2]
R.uniq([1, '1']) // => [1, '1']
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']) // => ['foo', baz]
values(obj: Object): Array
It returns array with of all values in obj
.
R.values({a: 1, b: 2}) // => [1, 2]
without(a: Array, b: Array): Array
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]
Rambda's typings are located at ./index.d.ts
, so your IDE should be able to pick it up without any additional actions.
You can use 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
R.ifElse
accept also boolean as condition argumenttypedDefaultTo
and typedPathOr
| Add R.pickAll
and R.none
Rambda
fully tree-shakeable| Edit Typescript definitionRollup
tree-shake | Remove R.padEnd
and R.padStart
R.reverse
mutates the arrayR.reject
and R.without
(PR#41 PR#42) | Remove 'browser' field in package.json
due to Webpack bug 4674R.forEach
and R.times
Typescript
definitionsdefaultTo
to a new method typedDefaultTo
; make defaultTo
follow Ramda spec; add pathOr
; add typedPathOr
.R.pipe
PR#35R.isNil
Webpack
with Rollup
- PR29R.tap
and R.identity
R.all
, R.allPass
, R.both
, R.either
and R.complement
yarn test
before yarn publish
the hard wayR.always
, R.T
and R.F
concat
, padStart
, padEnd
, lastIndexOf
, toString
, reverse
, endsWith
and startsWith
methodsR.ifElse
R.not
, R.includes
| Take string as condition for R.pick
and R.omit
R.values
R.omit
R.curry
, which used to return incorrectly function
when called with more argumentses2015
; Approve PR #10 - add R.addIndex
to the APIPromise
support for R.type
R.reduce
to the APIcurry
to partialCurry
; add new method curry
, which works just like Ramda's curry
docsify
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
yarn 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
yarn run lint modules/endsWith.js
yarn run lint __tests__/endsWith.js
Submit PR
Expect response within 2 days.
Running benchmarks
yarn run benchmark all
yarn run benchmark add compose filter
Libraries using Rambda
Articles about Rambda
FAQs
Lightweight and faster alternative to Ramda with included TS definitions
The npm package rambda receives a total of 551,776 weekly downloads. As such, rambda popularity was classified as popular.
We found that rambda demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.
Security News
Research
Socket's threat research team has detected five malicious npm packages targeting Roblox developers, deploying malware to steal credentials and personal data.