DEV Community

Alessio Michelini
Alessio Michelini

Posted on • Updated on

A practical example of how to use Currying in Javascript

A lot of times I see explanations of concepts in X language that, while I get what they do, I think "where the hell I can use that?".
Or some other times the examples used are too theoretical that it makes hard for you to understand when to use it in your day-to-day coding.

One of those concepts that I found confusing in the past was Currying.

If you don't know what Currying is, essentially is a programming technique where you take a function with multiple arguments, and you turn it into smaller sequential functions where you pass one argument at a time.

And you will probably see some examples like this one:

// your normal function
const add = (a, b) => {
  return a + b;
}

console.log(add(1,2)); // 3

// using currying
const add = (a) => {
  return (b) => {
    return a + b;
  }
}

console.log(add(1)(2)); // 3
Enter fullscreen mode Exit fullscreen mode

And if you are like me, you are probably thinking "why on earth I should use the second case?".

And the answer is that you shouldn't.

But not because currying is pointless, is just because that example is rather unpractical in my opinion.

When you should use currying

Now, let's say that we have an array of objects, something like this:

const list = [
  {
    id: 1,
    name: 'Steve',
    email: 'steve@example.com',
  },
  {
    id: 2,
    name: 'John',
    email: 'john@example.com',
  },
  {
    id: 3,
    name: 'Pamela',
    email: 'pam@example.com',
  },
  {
    id: 4,
    name: 'Liz',
    email: 'liz@example.com',
  },
];
Enter fullscreen mode Exit fullscreen mode

And you want to remove one of the objects if a specific property matches a value, for example if the object name property is equal "John", you want to filter it out.
The simplest way is to do it in this way:

const noJohn = list.filter(item => item.name !== 'John');
console.log(noJohn);
/**
[
  { id: 1, name: 'Steve', email: 'steve@example.com' },
  { id: 3, name: 'Pamela', email: 'pam@example.com' },
  { id: 4, name: 'Liz', email: 'liz@example.com' }
]
*/
Enter fullscreen mode Exit fullscreen mode

That works, but it's not reusable because you are hardcoding the name you want to remove.
A better way is to wrap it into a function and pass the name as an argument:

const filterByName = (list, name) => {
  return list.filter(item => item.name !== name);
}

console.log(filterByName(list, 'John'));
/**
[
  { id: 1, name: 'Steve', email: 'steve@example.com' },
  { id: 3, name: 'Pamela', email: 'pam@example.com' },
  { id: 4, name: 'Liz', email: 'liz@example.com' }
]
*
Enter fullscreen mode Exit fullscreen mode

Now, imagine that you are going to use the same filter function in two or more places in the same code, or maybe you want to keep the code DRY and you want to place the filtering in a variable on its own. You could try this:

const filtering = item => item.name !== name;

const filterByName = (list, name) => {
  return list.filter(filtering);
}
Enter fullscreen mode Exit fullscreen mode

But the above will throw you an error as the filtering will have no clue of what name is.

And here is where currying comes to action!

So you will need to change the above code to this:

// we add another function on top of the previous
const filtering = (name) => (item) => item.name !== name;

const filterByName = (list, name) => {
  return list.filter(filtering(name));
}

console.log(filterByName(list, 'John'));
/**
[
  { id: 1, name: 'Steve', email: 'steve@example.com' },
  { id: 3, name: 'Pamela', email: 'pam@example.com' },
  { id: 4, name: 'Liz', email: 'liz@example.com' }
]
*
Enter fullscreen mode Exit fullscreen mode

So what happened? The filtering function, has a top layer function, that accept the name as input, then return a new function that then accept the item as argument.

Then the filter function will run the result of the filtering(name), which is a function, and it will pass down the item.

If we use the old fashion function syntax, for old timers like me, it would be translated to something like this:

function filterByName(list, name) {
  return list.filter(function(nameToFilter) {
    // nameToFilter is declared at this point
    return function(item) {
      // item is declared here
      return item.name !== nameToFilter;
    }
  }(name));
}
Enter fullscreen mode Exit fullscreen mode

I hope that this explained a little bit better how currying works in Javascript.

Discussion (9)

Collapse
lukeshiru profile image
LUKESHIRU

Remember that for arrow functions you don't need the return keyword, so your curried functions would look even cleaner:

const add = a => b => a + b;
Enter fullscreen mode Exit fullscreen mode

Also, the filtering example can use currying even further:

// Generic curried functions
const filter = predicate => array => array.filter(predicate);
const filterBy = propertyName => propertyValue =>
    filter(item => item[propertyName] === propertyValue);

// Usage:
const filterByName = filterBy("name");
const filterByNameJohn = filterByName("John");

console.log(filterByNameJohn(list));
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
716green profile image
Bob Bass

Wow, I'm a full-time JavaScript developer and I am familiar with currying, if I'm being honest it's something I picked up when I was studying for interviews.

I recall seeing the double arrow function once and not making the connection that it was a curried function.

It's pretty wild to make that connection now.

Collapse
darkmavis1980 profile image
Alessio Michelini Author • Edited on

Hi! Yes I know, that's what I would normally do, but I feel that with the limited width of dev.to pages, using the return makes it a bit more readable ;-)
But yeah, in my normal coding I would do as you suggested

Collapse
kjk93 profile image
Kyle Kravette

Thank you so much for putting this into a practical application! Much easier to read and understand.

Collapse
gokhanipek profile image
Gökhan İpek

Thanks for the article, when learning a new concept there are times when people think like 'okay it looks cool but where/when should I use it' currying is probably one of the top concepts when it comes to this.
I remember thinking of a useful usage case to add to my article that's explaining currying, months ago. I'll bookmark yours and show people when I talk about it next time!

Collapse
marcelx8 profile image
Marcelx8

Great example, thanks!

Collapse
nicm42 profile image
Nic

Now I get why you'd use currying, thank you.

Collapse
itsraghz profile image
Raghavan alias Saravanan Muthu

Good one. Thank you for sharing.

Collapse
andrewpierno profile image
Andrew Pierno • Edited on

This is a solid tutorial, Alessio!
Would you be interested in writing some tutorials for our companies? Happy to pay!
You can either DM me on twitter at twitter.com/AndrewPierno or fill out this little airtable form airtable.com/shrN6S3NMZ7oxRXTt.