0

How can I create a function in javascript return like this:

sum(1,2) //3 
sum(1,3)(2) // 6

I have tried with this code but it wrong?

function(a,b) {
  return (c) {
   return a + b + c;
  }
}
4
  • Currying a function that takes infinite arguments? Commented Apr 14, 2021 at 6:25
  • That javascript snippet is not syntactically valid because it needs to be function(c) {... Commented Apr 14, 2021 at 6:28
  • 2
    Not possible. In sum(1,2) //3, sum(1,2) returns a number. But in sum(1,2)(3) //6, sum(1,2) returns a function. JavaScript functions can't return different things based on how you intend to use their return value. Commented Apr 14, 2021 at 6:28
  • You either need another () at the end or you need to override toStirng or valueOf of the function Commented Apr 14, 2021 at 6:29

4 Answers 4

1

You could override the valueOf of the inner function and sum the arguments (reference)

function sum(...a) {

  function inner(...b) {
    a.push(...b)
    return inner;
  }
  
  inner.valueOf = () => a.reduce((c, d) => c + d, 0)
  return inner
}

console.log( +sum(1, 2) )
console.log( +sum(1, 3)(2) )
console.log( +sum(1, 2, 3)(4, 5)(6) )

You could use rest syntax to collect all the parameters and return a function. This needs another () at the end to terminate the recursion

const sum = (...a) => (...b) => b.length 
                                ? sum(...a, ...b) 
                                : a.reduce((c, d) => c + d, 0)

const sum = (...a) => (...b) => b.length 
                                ? sum(...a, ...b) 
                                : a.reduce((c, d) => c + d, 0)

console.log( sum(1,2)() )
console.log( sum(1,3)(2)() )
console.log( sum(1,2,3)(4,5)(6)() )

Sign up to request clarification or add additional context in comments.

2 Comments

The .toString() trick only works with the StackSnippet console output. Open the browser one and you'd just see a reference to the function printed. You'd need to manually convert it in any "real" code so, maybe overriding .valueOf() (or @@toPrimitive) would be a better idea to do sum(1)(2, 3) * 4 or something.
There is a bit of a weakness in this approach as the same function inner is returned and modified. So, x = sum(1, 2); y = +x(3); z = +x(3); now produces y = 6 and z = 9 instead of the same number. So, separating out the steps and re-using them might lead to the wrong result: basePrice = sum(1, 2); totalWithoutShipping = +basePrice(1); totalWithShipping = +basePrice(3);
0

Since you are not going for fixed length arguments, I can only think of using a hack like below where we finally still return a function (summer) but modify the valueOf() for the inner function summer so that in the end when we do an operation like *1 on the last returned function which will be summer only, summer.valueOf() get's invoked and we can see the total sum till now.

function sum(...args){
   function summer (...args2){
   let argsArr = [...args,...args2];
       return sum(...argsArr)
   }
   summer.valueOf=()=>args.reduce((acc,curr)=>acc+=curr,0);
   return summer;
}


console.log(sum(1,2)*1);
console.log(sum(1,2)(3)*1);
console.log(sum(1,2)(3)(4,5,6,7,8)(9,10,11)*1);

Comments

0

The result of a function cannot vary based on whether it is called again or not. So, it is not possible to have sum(1, 2) that returns a number in one case but a function if called as sum(1, 2)(3).

However, since functions are objects in JavaScript, you can have the function return a new function with a property that has the total:

const sum = (function calculate() {
  return function running(...xs) {
    const total = xs.reduce((sum, x) => sum + x, running.total ?? 0);
    return Object.assign( calculate(), { total } )
  }
})();

console.log( sum(1, 2).total );
console.log( sum(1, 3)(2).total );
console.log( sum(1, 2, 3)(4, 5)(6).total );
console.log( sum(1, 2, 3, 4, 5, 6).total );
console.log( sum(1)(2)(3)(4)(5)(6).total );

const x = sum(1, 2);
console.log( x(3).total );
console.log( x(3).total );

This uses a self-referencing IIFE to keep things clean, as there is no need for an extra function. However, it is otherwise equivalent to:

function calculate() {
  return function running(...xs) {
    const total = xs.reduce((sum, x) => sum + x, running.total ?? 0);
    return Object.assign( calculate(), { total } )
  }
}

const sum = calculate();

The calculate() function always returns a new running() function that keeps track of the total and itself returns a new running() function with the total attribute. When running() calculates the sum, it uses the total it has or a zero if no total was present (first time calculate was executed).

   sum(1, 2, 3) (4, 5) (6) .total --> 21
   ^^^^^^^^^^^^ ^^^^^^ ^^^
              |      |   |
total = 6  <--+      |   |
total = 6 + 9  <-----+   |
total = 15 + 6 <---------+

If you prefer, you can also make sure that the function is convertible to a primitive number by overriding the valueOf and @@toPrimitive methods. Technically, you do not need both but does not hurt to be more thorough:

const sum = (function calculate() {
  return function running(...xs) {
    const total = xs.reduce((sum, x) => sum + x, running.total ?? 0);
    return Object.assign( calculate(), { 
      total,
      valueOf: () => total,
      [Symbol.toPrimitive]: () => total,
    })
  }
})();

const sum = (function calculate() {
  return function running(...xs) {
    const total = xs.reduce((sum, x) => sum + x, running.total ?? 0);
    return Object.assign( calculate(), { 
      total,
      valueOf: () => total,
      [Symbol.toPrimitive]: () => total,
    })
  }
})();

console.log( +sum(1, 2) );
console.log( +sum(1, 3)(2) );
console.log( +sum(1, 2, 3)(4, 5)(6) );
console.log( +sum(1, 2, 3, 4, 5, 6) );
console.log( +sum(1)(2)(3)(4)(5)(6) );

const x = sum(1, 2);
console.log( +x(3) );
console.log( +x(3) );

console.log( sum(1, 2) + sum(3) + sum(4)(5) );

Comments

0

Follow the below code :-

function currying(first, ...other){
     var argmnts = [first, ...other];
     return function interalCurrying(first, ...other) {
               if(first){
                   argmnts = [...argmnts , first, ...other]; 
                   return interalCurrying;
                } else {
                   return argmnts.reduce((acc , val)=>{
                          return acc+val;
                        },0); 
               }
       }   
}
console.log(currying(2,3)(4)());
console.log(currying(2)(3)(4)());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.