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;
  }
}
kalipts
  • 1,097
  • 1
  • 9
  • 21
  • [Currying a function that takes infinite arguments](https://stackoverflow.com/q/35039020)? – VLAZ Apr 14 '21 at 06:25
  • That javascript snippet is not syntactically valid because it needs to be `function(c) {...` – Trevor Kropp Apr 14 '21 at 06: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. – Amadan Apr 14 '21 at 06:28
  • You either need another `()` at the end or you need to override `toStirng` or `valueOf` of the function – adiga Apr 14 '21 at 06:29

4 Answers4

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)() )
adiga
  • 34,372
  • 9
  • 61
  • 83
  • 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. – VLAZ Apr 14 '21 at 06:44
  • 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);` – VLAZ Apr 14 '21 at 07:09
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);
Lakshya Thakur
  • 8,030
  • 1
  • 12
  • 39
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) );
VLAZ
  • 26,331
  • 9
  • 49
  • 67
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)());
Sunny Goel
  • 1,982
  • 2
  • 15
  • 21