Skip to content

Instantly share code, notes, and snippets.

@vvgomes
Last active August 10, 2021 18:10
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save vvgomes/451ea5ca2c65e87c92e4 to your computer and use it in GitHub Desktop.
Save vvgomes/451ea5ca2c65e87c92e4 to your computer and use it in GitHub Desktop.
Ramda vs Lodash
var _ = require("lodash");
var R = require("ramda");
var companies = [
{ name: "tw", since: 1993 },
{ name: "pucrs", since: 1930 },
{ name: "tw br", since: 2009 }
];
var r1 = _(companies).chain()
.filter(function(c) {
return c.name.split(" ")[0] === "tw";
})
.map(function(c) {
return {
name: c.name.toUpperCase(),
since: c.since
};
})
.sortBy(function(c) {
return c.since;
})
.reverse()
.value();
console.log("with lodash:", r1);
var r2 = R.compose(
R.reverse,
R.sortBy(R.prop("since")),
R.map(R.over(R.lensProp("name"), R.toUpper)),
R.filter(R.where({ name: R.test(/^tw/) }))
)(companies);
console.log("with ramda:", r2);
@jdalton
Copy link

jdalton commented Oct 2, 2015

with lodash

var r1 = _(companies)
  .filter(_.flow(_.property("name"), _.partial(_.startsWith, _, "tw")))
  .map(_.clone)
  .each(function(o) { o.name = o.name.toUpperCase(); })
  .sortByOrder("since", "desc")
  .value();

or with lodash-fp

var r3 = _.flow(
  _.filter(_.flow(_.property("name"), _.startsWith("tw"))),
  _.map(_.clone),
  _.each(function(o) { o.name = o.name.toUpperCase(); }),
  _.sortByOrder("since", "desc")
)(companies);

or combo'd with ES6 arrow functions

var r4 = _.flow(
  _.filter(_.flow(_.property("name"), _.startsWith("tw"))),
  _.map(o => ({ name: o.name.toUpperCase(), since: o.since })),
  _.sortByOrder("since", "desc")
)(companies);

@vvgomes
Copy link
Author

vvgomes commented Oct 25, 2015

Better than my Lodash version. But still, not fully point-free (even with Lodash-fp or ES6). Plus, "flow" doesn't map well to the function composition if compared to Ramda's "compose".

@larrybotha
Copy link

@vvgomes lodash-fp comes with compose, too. flow is just a reversed order of functions - perhaps for those not familiar with algebra, or for long lists of functions.

@qiansen1386
Copy link

Sorry, I am quite new for functional programming, I don't quite see the point of using Ramda.
With fluent API, we chain everything up in a begin->end order.
With Ramda compose, we seem have to reverse the order? Does it make the function group even harder to read?
To find out the beginning of entire block we need to jump all the way to the most inner function of last clause....

@mnn
Copy link

mnn commented Oct 10, 2016

@qiansen1386 Can't comment on "Ramda vs Lodash" (I am familiar with Lodash, but not so much with Ramda), but in Haskell (FP beast) I see it is common to use fn composition and actually prefer it even thought there are possibilities (in std. library and beyond) to use reversed functional composition. In light of this I tend to think it is just a matter of taste/habit which approach to use. They are equivalent - func. composition in Ramda can be seen as func. application going from outside (compose(a, b, c)(x) ~ a(b(c(x)))) while flow of Lodash reminds me of pipe opreator from Linux |: flow(a, b, c)(x) ~ echo "$x" | a | b | c.

PS: I actually wrote a short article about this order of composition/application Scala vs Haskell way - http://mnn.github.io/blog/en/2016/Some-thoughts-of-Haskell-ewbie-going-from-Scala/.

PPS: One can use R.pipe in Rambda to achieve same order as _.flow from Lodash has.

@vipero07
Copy link

@vvgomes lodashFP can easily be point free if you make a to uppercase function, the only difference is ramda has such a utility function built in. Functions like that give Ramda a larger footprint, but also decrease the amount of code you need to write for common functions like that.

@qiansen1386 the reason compose is the reverse of pipe is because it is the mathematical concept of function composition. With understanding some basic math concepts like the identity, distributive, commutative, and associative properties you can reorganize the composed functions to be more efficient. With pipe applying those properties is a bit more complicated as those properties aren't clear.

Some good examples of the benefits can be found here and here

@andrewshatnyy
Copy link

This all seems cool but in the end what is the performance difference. I mean when you end up working on the project where half of devs love Ramda and the other half worship Lodash the only reasonable argument is performance.

I heard that Lodash team has done some insane tricks to optimize the performance including using while loops instead of native to make iterators fast.
Has anyone done comprehensive benchmarking?

@Vbianshuman
Copy link

Vbianshuman commented Feb 1, 2017

Ramda seems to be better in terms of speed:

https://jsperf.com/ramda-vs-lodash
https://jsperf.com/ramda-vs-lodash/3

However, both are extremely sluggish as compared to native imperative code. Hopefully that will change in the future

@a-x-
Copy link

a-x- commented Feb 28, 2017

lodash.fp and more es6

var r5 = _.flow(
  _.filter(o => o.name.startsWith('tw')),
  _.map(o => ({ ...o, name: o.name.toUpperCase() })),
  _.sortByOrder('since', 'desc')
)(companies);

@tylerlong
Copy link

tylerlong commented Jun 27, 2017

A shorter Ramda version:

var r3 = R.pipe(
  R.filter(R.pipe(R.prop('name'), R.startsWith('tw'))),
  R.map(R.over(R.lensProp('name'), R.toUpper)),
  R.sort(R.descend(R.prop('since'))),
)(companies)

@qiansen1386

With Ramda compose, we seem have to reverse the order? Does it make the function group even harder to read?

You can use Ramda pipe instead of compose.

@MichelML
Copy link

Thanks for the battle this is pretty interesting (and entertaining haha!)

@hyperscientist
Copy link

hyperscientist commented Aug 30, 2017

And if we strip @a-x- version of unnecessary underscores… ;-)

var r5 = companies
  .filter(c => c.name.startsWith("tw"))
  .map(c => ({ ...c, name: c.name.toUpperCase() }))
  .sort((a, b) => b.since - a.since);

Someone would have to try extra hard to convince me that 9 function invocations of 9 different Ramda methods (all of which you along with all present and future team members have to have memorised) is better in any aspect…

@hillerstorm
Copy link

@kamiltrebunia what if companies or c.name is null or undefined? ;) lodash and ramda handles that for you

@jaunkst
Copy link

jaunkst commented Oct 3, 2017

@hillerstorm yep, and the first function can easily be a filter or reducer to eliminate invalid entities

@omeid
Copy link

omeid commented Feb 18, 2018

@hillerstorm: Got you covered!

var r5 = (companies || [])
  .filter(c => c.name && c.name.startsWith("tw"))
  .map(c => ({ ...c, name: c.name.toUpperCase() }))
  .sort((a, b) => b.since - a.since);

@marano
Copy link

marano commented Oct 11, 2018

Even though Ramda is definitely more powerful, and I do prefer Ramda over lodash, I've found that for a lot of common operations lodash is simpler to use. It handles many real world cases that Ramda doesn't. For instance, when you iterate object properties with lodash it will skip "hidden" properties (that start with _) by default. It also performs much better on some operations, of course it doesn't really matter most of the time.

@hnordt
Copy link

hnordt commented Apr 28, 2019

An even shorter Ramda version:

const r3 = R.pipe(
  R.filter(R.where({ name: R.startsWith('tw') })),
  R.map(R.evolve({ name: R.toUpper })),
  R.sort(R.descend(R.prop('since')))
)

r3(companies)

@enoh-barbu
Copy link

enoh-barbu commented Dec 16, 2019

in the lodash example you said c.name.split(" ")[0] === "tw" but in the ramda's one you've put a regex R.test(/^tw/) . Really?
The same regex could be also applied in the first case, natively /^tw/.test(name) which is actually shorter.

@vvgomes
Copy link
Author

vvgomes commented Dec 16, 2019

But that would not be point-free. The point is not being shorter. The point is being point-free, auto-curried, composable.

Thanks for adding to the discussion :)

@enoh-barbu
Copy link

Oh, I wasn't aware of this comparison.

@qjnz
Copy link

qjnz commented Jan 12, 2021

how about from performance perspective? anyone has done it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment