Javascript function challenge add(1,2) and add(1)(2) both should return 3
Asked Answered
A

8

6

A friend of mine challenged me to write a function that works with both of these scenarios

add(2,4)  // 6
add(2)(4) // 6

My instinct was the write an add() function that returns itself but I'm not sure I'm heading in the right direction. This failed.

function add(num1, num2){
    if (num1 && num2){
        return num1 + num2;
    } else {
        return this;
    }
}

alert(add(1)(2));

So I started reading up on functions that return other functions or return themselves.

I am going to keep trying, but if someone out there has a slick solution, I'd love to see it!

Acnode answered 9/7, 2015 at 0:49 Comment(2)
@MarkC. Questions like this one need an objective winning criterion to be on topic on PPCG. In its current state, this question is a bad fit and has been put on hold.Amortizement
I think @Partric Roberts has better answer, can you mark his answer correct?Murr
H
6

There is an article on Dr.Dobs Journal about "Currying and Partial Functions in JavaScript" which describes exactly this problem.

One solution found in this article is:

// a curried add
// accepts partial list of arguments
function add(x, y) {
     if (typeof y === "undefined") { // partial
        return function (y) {
              return x + y;
        };
     }
   // full application
   return x + y;
}
Hodman answered 9/7, 2015 at 9:55 Comment(1)
Instead of typeof y === "undefined" try arguments.length < 2, in case undefined is explicitly passed to y.Willowwillowy
W
11

I wrote a curried function whose valueOf() method and function context (this) are bound with the sum no matter how many arguments are passed each time.

/* add function */
let add = function add(...args) {
  const sum = args.reduce((acc, val) => acc + val, this);
  const chain = add.bind(sum);
  chain.valueOf = () => sum;
  return chain;
}.bind(0);

/* tests */
console.log('add(1, 2) = ' + add(1, 2));
console.log('add(1)(2) = ' + add(1)(2));
/* even cooler stuff */
console.log('add(1, 2)(3) = ' + add(1, 2)(3));
console.log('add(1, 2, 3)(4, 5)(6) = ' + add(1, 2, 3)(4, 5)(6));
/* retains expected state */
let add7 = add(7);
console.log('let add7 = add(7)');
console.log('add7(3) = ' + add7(3));
console.log('add7(8) = ' + add7(8));

The reason why both mechanisms are required is because the body of add() must use the called function's bound context in order to access the sum of the intermediate partial application, and the call site must use the valueOf() member (either implicitly or explicitly) in order to access the final sum.

Willowwillowy answered 9/7, 2015 at 1:6 Comment(3)
Is there any way to make this work without console.log() ?Gastrolith
@SarathSNair yes, cast using Number() or unary +.Willowwillowy
How to do that? I want to get 6 if I call just add(1,2)(3) without console.log('add(1, 2)(3) = ' + add(1, 2)(3));Gastrolith
H
6

There is an article on Dr.Dobs Journal about "Currying and Partial Functions in JavaScript" which describes exactly this problem.

One solution found in this article is:

// a curried add
// accepts partial list of arguments
function add(x, y) {
     if (typeof y === "undefined") { // partial
        return function (y) {
              return x + y;
        };
     }
   // full application
   return x + y;
}
Hodman answered 9/7, 2015 at 9:55 Comment(1)
Instead of typeof y === "undefined" try arguments.length < 2, in case undefined is explicitly passed to y.Willowwillowy
R
0
function add(num1, num2){
    if (num1 && num2) {
        return num1 + num2;
    } else if (num1) {
        return function(num2){return num1 + num2;};
    }
    return 0;
}
Ryannryazan answered 9/7, 2015 at 0:53 Comment(2)
add(0,x) always returns 0, regardless of the value of x. add(0)(x) always results in a TypeErrorGeorama
@Adam: right. in that case it would be better to check for undefined, or arguments.length which is mentioned in the answers above.Ryannryazan
F
0

The concept that you're looking for is called currying and it has to do with function transformation and partial function application. This is useful for when you find yourself calling the same function over and over with mostly the same arguments.

An example of implementing add(2)(6) via currying would look something like this...

function add(x,y) { 
  if (typeof y === 'undefined') {
    return function(y) {
      return x + y;
    }
  }
}

add(2)(4); // => 6

Additionally, you could do something like this...

var add6 = add(6);
typeof add6; // => 'function'
add6(4);     // => 10
Fountainhead answered 9/7, 2015 at 1:7 Comment(0)
W
0
var add = function(){
  // the function was called with 2 arguments
  if(arguments.length > 1)
    arguments.callee.first_argument = arguments[0];

  // if the first argument was initialized
  if(arguments.callee.first_argument){
    var result = arguments.callee.first_argument + arguments[arguments.length - 1];
    arguments.callee.first_argument = 0;

    return result;
  }else{// if the function was called with one argument only then we need to memorize it and return the same function handler 
    arguments.callee.first_argument = arguments.callee.first_argument || arguments[0];
    return arguments.callee;
  }
}
console.log(add(2)(4));
console.log(add(2, 4));

An extended solution which depends on the environment:

function add(){
  add.toString = function(){
    var answer = 0;
    for(i = 0; i < add.params.length; i++)
      answer += add.params[i];
    return answer;
  };

  add.params = add.params || [];
  for(var i = 0; i < arguments.length; i++)
    add.params.push(arguments[i])

  return add;
}

console.log(add(2)(4)(6)(8))
console.log(add(2, 4, 6, 8));
Withers answered 9/7, 2015 at 1:28 Comment(0)
M
0

We can use the concept of closures which is provided by Javascript.

Code snippet:

function add(a,b){

    if(b !== undefined){

        console.log(a + b);
        return;

    }

    return function(b){
        console.log(a + b);
    }

}

add(2,3);
add(2)(3);
Mafala answered 28/5, 2016 at 18:6 Comment(0)
P
0

In general you need to have an agreement whether the function should return a function (for calling with more arguments) or the end result. Imagine the add function would have to work like this as well:

add(1, 2, 3)(4, 5)  // -> 15

...then it becomes ambiguous, because you might want to call again:

add(1, 2, 3)(4, 5)(6)  // -> 21

...and so add(1, 2, 3)(4, 5) should have returned a function, and not 15.

You could for instance agree that you have to call the function again, but without arguments, in order to get the numeric result:

function add(...args) {
    if (args.length === 0) return 0;
    let sum = args.reduce((a, b) => a+b, 0);
    return (...args) => args.length ? add(sum, ...args) : sum; 
}

console.log(add()); // 0
console.log(add(1,2,3)()); // 6
console.log(add(1,2,3)(4,5)()); // 15
console.log(add(1,2,3)(4,5)(6)()); // 21
Precessional answered 21/2, 2021 at 18:27 Comment(0)
Q
-1

One may think that he/she has to invoke the same function two times, but if you think deeply you will realize that the problem is pretty straight forward, you have to invoke the add function one time then you need to invoke what ever the add function returns.

function add(a){
    return function(b){
        return a+b;
    }
}
console.log(add(20)(20));
//output: 40

you can return function as many as time you want. suppose for y = mx+c

const y= function (m){
    return function(x){
        return function (c){
            return m*x+c
        }
    }
}

console.log(y(10)(5)(10)); 
//out put: 60
Quadrangle answered 12/5, 2020 at 7:53 Comment(1)
Without deep thinking, I note that the OP wanted add(2,4) to return 6.Precessional

© 2022 - 2024 — McMap. All rights reserved.