Differences between Lodash and Ramda | Aryan Agarwal

Differences between Lodash and Ramda

As to this part of the question:

Which one is better?

There is no way anyone here can answer that definitively. It depends on your requirements, your history, your programming taste, and a million even more fuzzy factors.

I'm biased in favor of Ramda, but I still won't try to tell you which one is better.

But there are significant overlaps in capability between these two and significant differences in underlying philosophy.

Both are grab-bags of utility function, with little or no cohesion between the functions. That is, they are libraries and not frameworks. They don't at all try to determine how you write or organize your code.

They have a great deal of overlap. Among the 305 current lodash functions and the 261 current Ramda ones, there are 103 with shared names, and almost always, similar purposes. Probably half the remaining functions are found in the other library under different names.

A Brief History

Although there were some experimental functional libraries before it, Underscore was the one that brought many of these concepts to the mainstream of Javascript. It stood alone for a while as the undisputed leader in this space.

But there were problems with its performance. Eventually, lodash was created in large part to try to do the same thing with better performance. By creating custom versions of classic functions like map, lodash was quickly able to outperform Underscore. But it started as a drop-in replacement for Underscore and kept that focus for a long while.

The Ramda founders were impressed with the ideas in Reginald Braithwaite's JavaScript Allongé and created Ramda as an educational tool to help turn these ideas into a pragmatic library. They were less focused on performance, and more on a clean API and functional composition, as well as immutable data and side-effect-free coding.

Through an odd series of events, it eventually grew quite popular. These days you don't hear much about Underscore. It's still out there, and still used a fair bit, but you don't hear much buzz.

For most users, lodash fills that space well. Ramda grew quickly but seems to have settled down at about 20 - 25% of the downloads that lodash gets.

But all three libraries continue to grow in capability and in usage, with Underscore and Ramda about the same usage, far below lodash.

At one point, lodash created a version that tries to take into account some of Ramda's core concerns. I personally have never used lodash-fp so I can't speak to how successfully it does this.

Philosophy

lodash

Lodash focuses on flexibility and performance. Its focus, as its creator once described, is on

providing quality utility methods to as many devs as possible with a focus on consistency, compatibility, customization, and performance.

While no general-purpose library can ever be quite as fast as custom-written code, lodash gets as close as possible.

And lodash is flexible. As described several years ago

Consider lodash's filter: It accepts an array, object, or string for its collection, a function, object, string, or nothing at all for its callback, and an object or nothing at all for its thisArg. You're getting 3 * 4 * 2 = 24 functions in one!

Ramda

Ramda is less concerned with performance, and more with simple and clean API design. The idea for Ramda is that a function should do one thing only, and should have a single clear interface.

Ramda's filter function takes a predicate function and an object of a filterable type and returns another object of that type. (Of course, this raises the question of what constitutes a filterable type, but that's for its documentation.)

Ramda's focus is on making it simple to compose functions, to work as though all data is immutable, and to avoid side effects. It also incorporates other functional programming concerns, such as supplying lenses and working with the algebraic types of the FantasyLand specification.

Key Differences

Lodash functions mostly take their data first, followed by those things that work on the data, followed sometimes by optional arguments that change the behaviour.

Ramda puts the arguments least likely to change first. This means that in data-transformation functions the data is last. And Ramda avoids optional arguments altogether.

Ramda curries all its functions, as well as nearly all the functions it returns to you.

Lodash has a curry function, but you would need to call it explicitly. This is fairly central to Ramda's ideas on function composition.

Lodash focuses on reference equality.

Ramda focuses on value equality.

So while these are similar:

// lodash_.union ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [1, 2, 3, 4, 5, 7, 11]_.intersection ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [2, 3, 5]// RamdaR.union ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [1, 2, 3, 4, 5, 7, 11]R.intersection ([1, 2, 3, 4, 5], [2, 3, 5, 7, 11]) //=> [2, 3, 5]

these act very differently:

// lodash_.union ( [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}])//=> [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}, {x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]_.intersection ( [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]) //=> []// RamdaR.union ( [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}])//=> [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}, {x: 7}, {x: 11}]R.intersection ( [{x: 1}, {x: 2}, {x: 3}, {x: 4}, {x: 5}], [{x: 2}, {x: 3}, {x: 5}, {x: 7}, {x: 11}]) //=> [x: 2}, {x: 3}, {x: 5}]

So, Ramda's design is more closely aligned with functional systems but comes at a substantial performance price whenever equality-checking is involved. Lodash is perhaps two orders of magnitude faster than Ramda for such tasks.

Ramda is chiefly designed to build functions through composition, in short, or long pipelines.

Lodash is chiefly designed to work with imperative code.

You can use either library for either job, but typical Ramda code might look like

const myFn = R.pipe ( R.fn1, R.fn2 ('arg1', 'arg2'), R.fn3 ('arg3'), R.fn4)

where the equivalent lodash code might look like

const myFn = (x, y) => { const var1 = _.fn1 (x, y) const var2 = _.fn2 (var1, 'arg1', 'arg2') const var3 = _.fn3 (var2, 'arg3') return _.fn4 (var3)}

Lodash's focus on performance means that it will supply many well-optimized functions. For instance, Lodash has all of these functions:

isArgumentsisArrayisArrayBufferisArrayLikeisArrayLikeObjectisBooleanisBufferisDateisElementisEqualisEqualWithisErrorisFiniteisFunctionisIntegerisLengthisMapisMatchisMatchWithisNaNisNativeisNullisNumberisObjectisObjectLikeisPlainObjectisRegExpisSafeIntegerisSetisStringisSymbolisTypedArrayisUndefinedisWeakMapisWeakSet

Ramda, by contrast, expects you to supply more arguments to common functions. It has only

isisEmptyisNil

But it's is handles nearly all the cases above, either by calling explicitly:

is(Array, [1, 2, 3])

or by partially applying it to make a reusable function

const isArray = is(Array)

Some of these differences presumably vanish with lodash-fp. But I don't get any real sense that lodash-fp is a major part of the landscape. And for lodash proper, we can see that the libraries are quite different.

Performance

The question asked about benchmarks. I know of no comprehensive benchmark suite. It seems except when the issue of value-equality vs reference-equality is involved, lodash is 10 - 20% faster at most tasks. Ramda is not as hyper-optimized as lodash, but it is written with performance in mind, so long as its other more fundamental criteria are met.

In those cases that do involve value-vs-reference equality, the lodash team can point to much higher speeds, but the Ramda team would probably respond that getting the wrong answer quickly is hardly a win.

Target Audience

Underscore helped bring functional programming tools to Javascript. It is a general-purpose utility library and was designed for any JS developer who wants to be more productive. Lodash inherited this focus. Both are written with developer ergonomics as a central focus.

Ramda has a much more restricted audience. It is for those who want to take not just specific tools from functional programming, but who want to take its more fundamental ideas such as functional purity and data immutability.

It is aimed both at JS developers who want to move to a more FP style and at those from FP language who want to use JS in a familiar manner. It is written with simplicity and a central focus.

Summary

These libraries overlap a great deal in functionality. But their designs, their underlying philosophies, and their developer experience are quite different.

If you're choosing between them, there is a right answer for you, based on the above, but there is no clear definitive best library for everyone.

And you should also consider whether you need one at all. It's not hard to develop your own list of reusable functions to be included in whatever projects need them. The platform has become much more capable than when these libraries were conceived.


Checkout related posts on: