Lodash to Ramda example

An argument for trying Ramda if you love _

I love lodash JavaScript library. It allows me to write short, elegant code without most common logical bugs. Recently, I have tried a different functional library Ramda that has a few additional killer features:

1 - every function is curried by default. This makes it super easy to prefill some arguments with values (in left to right order), leaving the rest free

1
2
var add10 = R.add(10);
add10(2); // 12

Of course, lodash has curry too, but it is not turned on for its own functions.

2 - Ramda includes several functions missing from lodash (but are part of the separate lodash-contrib library). There are logical operators, simple arithmetic, but most important: pipe function. It is the opposite of compose and produces code that is very easy to read.

1
2
3
4
var x2 = R.multiply(2);
var plus10 = R.add(10);
var x2plus10 = R.pipe(x2, plus10);
x2plus10(4); // 4*2 + 10

3 - Ramda puts data at the last position in each function. I like this approach and believe it can lead to more elegant code. In most cases, I first create a function that is like a pipeline, then apply it to the actual data.

For example, let us imagine the following problem. We have an array of objects. Each object has a method that returns either a string or an array of strings. Our goal is to compose a list of unique values from all calls

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var items = [{
getName: function () {
return 'foo';
}
}, {
getName: function () {
return ['bar', 'foo'];
}
}, {
getName: function () {
return 'foo';
}
}];
// unique flat strings
// ['foo', 'bar']

Here is the solution using lodash

1
2
3
var list = _.uniq(_.flatten(_.map(items, function (v) {
return v.getName();
})));

Notice that the order of operations is a little weird. The actual data variable items is surrounded by the _ calls and a utility callback calling getNames(). We can rewrite this code to get more left to right ordering

1
2
3
4
5
var list = _(items)
.invoke('getName')
.flatten()
.uniq()
.value();

This goes against my principle to first create function from the individual steps, then call it on data.

Here is the same code using Ramda and my own functional-pipeline to fill missing invoke feature in Ramda (There is invoker but it requires all objects to have a common prototype. If anyone can suggest how to do this in Ramda, please let me know):

1
2
3
4
// fp comes from functional-pipeline
var getNames = R.map(fp('getName'));
var pipe = R.pipe(getNames, R.flatten, R.uniq);
pipe(items);

Update Same functionality can be achieved without fp using ramda.func method (thanks to @buzzdecafe for solution)

1
2
3
var getNames = R.map(R.func('getName'));
var pipe = R.pipe(getNames, R.flatten, R.uniq);
pipe(items);

I find Ramda's functional ordering more intuitive and almost 1 to 1 matching natural English sentence expressing my goal: get names, then flatten, then find unique values.

4 - Ramda's list iterators only pass the item by default. On the other hand JavaScript and Lodash pass the item and index to the callback function. This madness has burnt me several times.

1
2
3
4
5
6
['1', '2', '3'].map(parseInt);
// [ 1, NaN, NaN ]
_.map(['1', '2', '3'], parseInt);
// [ 1, NaN, NaN ]
R.map(parseInt, ['1', '2', '3']);
// [ 1, 2, 3] - works as expected

You can still have same callback with index and array instance by using R.map.idx and similar functions.

Having said all this, we are still keeping lodash in our production code for three reasons

  1. We are not going to rewrite lots of tested and familiar code. Both libraries can coexist fine.
  2. Lodash is very well tested and fast. It remains to be seen if Ramda is going to be developed at the same level as lodash.
  3. Next version of lodash (3.0.0) will include some of the features that might make it easier for the kind of functional pipeline programming I practice.

Update 1

The author of Ramda Scott Sauyet has posted an excellent essay on his philosophy behind writing Ramda the way it is. Read The Philosophy of Ramda.