Currying in JavaScript is a concept of functional programming in which we can pass functions as arguments (callbacks) and return functions without any side effects (Changes to program states).
What is currying?
In simple terms, in currying we return a function for each function invoked which excepts next argument inline. With the help of currying we can transform a function with multiple arguments into a sequence of nesting functions.
Example
//normal function sum(1, 2, 3) //should return 6 //currying function sum(1)(2)(3) //should return 6
If you notice in the currying function for each function call sum(1)
we are returning a function which accept next argument sum(1)(2)
and it again returns a function which accepts argument sum(1)(2)(3)
and so on.
There is no specified limit it to it how many times you return a function, also there are different variations to currying like the first function will accept the 2 arguments and the next function can accept any number of arguments and so.
//variations of currying sum(1)(2)(3) sum(1, 2)(3) sum(1)(2, 3) sum(1, 2, 3) //all of them should return same output //6
Now you may be wondering each function call returns a new function then how the value is returned from it. Well for that we have to decide a base condition in which should return the output like if no argument is passed in the next function call then return the value or if we have reached 5 arguments then return the value, etc.
sum(1)(2)(3)() sum(1, 2)(3)() sum(1)(2, 3)() sum(1, 2, 3)() //all of these should return 6 //OR //when we reach 5 arguments then return the value rather than new function sum(1, 2, 3, 4, 5) sum(1, 2)(3, 4, 5) sum(1)(2, 3, 4, 5) sum(1, 2, 3)(4, 5) sum(1)(2)(3)(4)(5) sum(1, 2, 3, 4)(5) //all should return 15
I was recently asked this question in interview to implement the same currying function for 4 arguments. When we have reached the limit return the value.
Note:- Maximum 4 arguments will only be passed.
sum(1, 2, 3, 4) sum(1)(2)(3)(4) sum(1, 2)(3, 4) sum(1, 2, 3)(4) sum(1)(2, 3, 4) //all should return 10
lets start implementing this.
First, lets handle the base case.
When the function is invoked in normal style sum(1, 2, 3, 4)
. In this all we have to do is check the number of arguments passed, if it is same as the limit provided return the sum of them.
const sum = (...args) => { //spread the arguments in storage array const storage = [...args]; //base case if(storage.length === 4){ return storage.reduce((a, b) => a + b, 0); } }
...args
is the rest operator which aggregates all the passed arguments as an array.
Once we have the array of arguments we have stored that in a variable inside the function storage = [...args]
.
The purpose of using is variable is that, when the arguments passed is less than the limit then we will use this further to store next argument in the closure.
Now if the length of the storage is 4 which means we have 4 arguments so return their sum.
Otherwise we have to return a new function every time until we have reached the limit.
const sum = (...args) => { //spread the arguments in storage array const storage = [...args]; //base case //if we have reached the limit if(storage.length === 4){ return storage.reduce((a, b) => a + b, 0); } //other wise return a function else{ //create an inner function const temp = function(...args2){ //get the arguments of inner function //merge them in existing storage storage.push(...args2); //if we have reached the limit //return the value if(storage.length === 4){ return storage.reduce((a, b) => a + b, 0); } //else return the same function again else{ return temp; } } //return the function return temp; } }
For that we have a created a closure using an inner function.
According to MDN-
A closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
Thus we will still be able to access the storage
variable from the returned function.
We merge the arguments of inner function in the existing storage and check if we have reached the limit or not in each call.
If we have reached the limit return the sum of them other wise return the same function again.
Input: const res = sum(1, 2, 3, 4); const res2 = sum(1)(2)(3)(4); const res3 = sum(1, 2)(3, 4); const res4 = sum(1, 2, 3)(4); const res5 = sum(1)(2, 3, 4); console.log(res, res2, res3, res4, res5); Output: 10 10 10 10 10
This is solved and it is working fine, after this interviewer asked me to update the code to return the value when invoked with empty arguments.
sum(1, 2, 3, 4)(); sum(1)(2)(3)(4)(); sum(1, 2)(3, 4)(); sum(1, 2, 3)(4)(); sum(1)(2, 3, 4)(); sum(); //10 10 10 10 10 0
For the last input when no argument is passed it should return 0.
The solution for this is quite straight forward, all we have to do is update the condition and instead of checking the no of arguments available in the storage, we have to check if there is any argument passed or not. If it is not passed then return the sum of arguments we have in storage.
const sum = (...args) => { //spread the arguments in storage array const storage = [...args]; //base case //if invoked without any argument if(storage.length === 0){ return 0; } //closure else{ //create an inner function const temp = function(...args2){ //get the arguments of inner function //merge them in existing storage storage.push(...args2); //if no arguments are passed //return the value if(args2.length === 0){ return storage.reduce((a, b) => a + b, 0); } //else return the same function again else{ return temp; } } //return the function return temp; } }
Input: const res = sum(1, 2, 3, 4)(); const res2 = sum(1)(2)(3)(4)(); const res3 = sum(1, 2)(3, 4)(); const res4 = sum(1, 2, 3)(4)(); const res5 = sum(1)(2, 3, 4)(); const res6 = sum(); console.log(res, res2, res3, res4, res5, res6); Output: 10 10 10 10 10 0