3

Problem: Create a function that sums two arguments together. If only one argument is provided, then return a function that expects one argument and returns the sum.

For example, addTogether(2, 3) should return 5, and addTogether(2) should return a function.

Calling this returned function with a single argument will then return the sum: var sumTwoAnd = addTogether(2); sumTwoAnd(3) returns 5.

If either argument isn't a valid number, return undefined.

Solution should return:

addTogether(2, 3) should return 5. addTogether(2)(3) should return 5. addTogether(2, "3") should return undefined. addTogether(2)([3]) should return undefined.

I tried everything I could, but the only thing that worked, and is purportedly the best solution so far is the following:

function addTogether() {
  "use strict";
  // check if argument(s) valid number
  var validateNum = function(num) {
    if(typeof num !== 'number') {
      return undefined;
    } else
      return num;
  };
  // is there is one argument or two
  if(arguments.length > 1) {
    var a = validateNum(arguments[0]);
    var b = validateNum(arguments[1]);
    if(a === undefined || b === undefined) {
      return undefined;
    } else {
      return a + b;
    }
  // if only one argument, return function that expects one argument and returns sum.
  } else {
    var c = arguments[0];
    // start here
    if(validateNum(c)) {
      return function(arg2) {
        if(c === undefined || validateNum(arg2) === undefined) {
          return undefined;
        } else {
          return c + arg2;
        }
      }; // belongs to return function(arg2) {}
    }
  }
}

addTogether(2)(3);
Maria Campbell
  • 418
  • 5
  • 9

7 Answers7

1

function addTogether(a, b) {
  if (typeof a == "number") {
    if (arguments.length == 1) {
      return b => addTogether(a, b);
    } else if (typeof b == "number") {
      return a + b;
    } 
  }
}

// as per OP's code
// returns 3
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
// returns NaN
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));
// returns undefined
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));

The following improvements have been suggested, but they would change the semantics of OPs code:

  • return undefined if a or b is NaN, as NaN is not a 'valid number'
  • return undefined if more than two arguments are provided instead of silently dropping them (thanks @PatrickRoberts)

If you don't mind returning a function for e. g. addTogether('x'), use:

function addTogether(a, b) {
  if (arguments.length == 1) {
    return b => addTogether(a, b);
  } else if (typeof a == "number" && typeof b == "number") {
    return a + b;
  }
}

This way, your will always return a function for one argument and Number or undefined for two or more arguments = more robust code.

For ES5 compatibility and if you don't mind addTogether(2)() returning a function, replace b => addTogether(a, b) with addTogether.bind(undefined, a) (thanks @PatrickRoberts).

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
le_m
  • 19,302
  • 9
  • 64
  • 74
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/114888/discussion-on-answer-by-le-m-is-possible-to-reduce-the-complexity-and-spaghetti). – George Stocker Jun 16 '16 at 23:08
1

You can use spread operator for improving your function and some Array functions like some or reduce :

In that way addTogether can accept more than one argument. If addTogether is called with one argument, the function returned can be called with more than one arguments too.

let isNotNumber = number=> typeof number != 'number';

let addTogether = function(...numbers){
  if(!numbers.length) return;
  if(numbers.length == 1){
    if(isNotNumber(numbers[0])) return;
    return function(...otherNumbers){
      if(otherNumbers.some(isNotNumber)) return;
      return otherNumbers.reduce((prev, curr)=> prev + curr, numbers[0]);
    }
  } else {
    if(numbers.some(isNotNumber)) return;
    return numbers.reduce((prev, curr)=> prev + curr);
  }
}

// Will return a value
console.log(addTogether(1,2,3));
console.log(addTogether(1)(2,3));

// Will return undefined
console.log(addTogether(1, [2]));
console.log(addTogether(1)('2'));
console.log(addTogether(1)([2]));
console.log(addTogether());
Jose Hermosilla Rodrigo
  • 3,513
  • 6
  • 22
  • 38
1

According to jscomplexity.org, OP's function has a cyclomatic complexity of 8 while the solution below have a cyclomatic complexity of 5 (based on the Babel ES5 transpilation).

This solution is functionally equivalent to OP's code, see tests below:

'use strict';

function addTogether(...augends) {
  if (augends.slice(0, 2).every(value => typeof value === 'number')) {
    switch (augends.length) {
    case 0:
      return;
    case 1:
      return (addend) => addTogether(augends[0], addend);
    default:
      return augends[0] + augends[1];
    }
  }
}

// should work (returns 3)
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
// should return NaN (not sure if this "works" or not)
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));
// should not work (returns undefined)
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));

For reference, here is my other solution which prohibits extraneous arguments and also tests against literal NaN values (which ironically are typeof "number"). Edit Unfortunately due to fixing the implementation for the test case console.log("addTogether(1)() = " + addTogether(1)());, it now has a cyclomatic complexity of 7:

'use strict';

function addTogether(...augends) {
  if (augends.every(value => typeof value === 'number' && !isNaN(value))) {
    switch (augends.length) {
    case 1:
      return (addend, ...addends) => addTogether(augends[0], addend, ...addends);
    case 2:
      return augends[0] + augends[1];
    }
  }
}

// should work (returns 3)
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
// should not work (returns undefined)
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
0

Why you don't provide arguments to function. I think that this is the simplest vay:

function add(arg1, arg2) {

    if (isNan(arg1) || isNan(arg2)) return undefined;

    if (!arg2) {
        return functionName(arg1);
    }

    return arg1 + arg2;
}
Blady214
  • 729
  • 6
  • 19
0

I guess you could do it like this. In case only 1 argument is provided, it would return the current function, but bind the first argument, so that you only have to provide the extra argument

'use strict';

function isNumeric(arg) {
  return typeof arg === 'number' && !isNaN(arg);
}

function sum(a, b) {
  if (arguments.length > 2) {
    return undefined;
  }
  if (isNumeric(a) && isNumeric(b)) {
    return a + b;
  }
  if (typeof b === 'undefined' && arguments.length === 1) {
    if (typeof a === 'undefined') {
      return undefined;
    } else if (isNumeric(a)) {
      return sum.bind(this, a); // returns the sum function with the a argument bound to the current a argument
    }
  }
  return undefined;
}

function test() {
  var args = '',
    result = sum,
    value;
  Array.prototype.forEach.call(arguments, function(argument) {
    value = argument;
    if (!argument) {
      argument = 'undefined';
    }
    args += '(';
    if (Array.isArray(argument)) {
      args += Array.prototype.join.call(argument, ', ');
    } else {
      args += argument.toString();
    }
    args += '}';
    if (typeof result === 'function') {
      if (Array.isArray(value)) {
        result = result.apply({}, value);
      } else {
        result = result.call({}, value);
      }
    }
  });
  console.log(`sum${args} = ${result}`);
}

test(2);
test([2, 3]);
test(2, 3);
test([2, undefined], 3);
test([2, "3"]);
test(2, [
  [3]
]);
test(2, "3");
test(2, [3]);
test([NaN, 2]);
Icepickle
  • 12,689
  • 3
  • 34
  • 48
  • You interpret NaN as 'not a valid number' while OP does. Still, I think your interpretation is more meaningful. We need OPs clarification. Also just out of curiosity: is the isArray() check necessary? – le_m Jun 15 '16 at 13:49
  • @le_m actually yeah, without the array or string check, they got both evaluated as a number – Icepickle Jun 15 '16 at 14:04
  • I don't think `sum(1, undefined)(2)` should work... better to check the length of `arguments`. – Patrick Roberts Jun 15 '16 at 14:07
  • @PatrickRoberts You are right, I updated the code a bit and added it as a test ;) – Icepickle Jun 15 '16 at 21:29
0

You should read about function currying, bind, call, and apply. Currying is applicable to ES6, but bind,call and apply run everywhere.

Details on the MDN site https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

In short, the solution you are looking for is:

function addTogether(a,b) {
    return a+b;
}
console.log(typeof addTogether); //-->function
console.log(typeof addTogether.bind(null,2)); //-->function
console.log(addTogether.bind(null,2)(3));//5
console.log(addTogether(2,3)); //-->5

Yeah, it IS that simple, not kidding!

  • To add, you should be willing to use the bind argument - this is a feature provided by Javascript for binding the scope and some of the arguments. (The null at the beginning binds the scope of this function.) – user6387024 Jun 15 '16 at 14:28
  • Note: you would of course still need to perform type checks to eliminate cases such as `addTogether(2, "3") == "23"` as per OPs request. – le_m Jun 15 '16 at 15:05
  • Noted. That's a simple **typeof a==='number' && typeof b==='number'** check. Aside, have voted +1 on the comment that suggests this should go into code review! – user6387024 Jun 15 '16 at 15:10
  • It took me some time to understand that this is a complexity-theory question and not a javascript question. :P Will let the response stay, might add to a discussion of how javascript simplifies complexity. – user6387024 Jun 15 '16 at 17:15
  • Well, the run-time complexity of all answers is identical. OP should probably clarify what kind of complexity the question refers to, probably "cyclomatic complexity". – le_m Jun 15 '16 at 17:22
0

This is not that short, but I overcome the challenge, I'm too happy to avoid sharing my solution:

function addTogether(...args) {
  if (args.length == 0) {
    //there is no args provided to the function .. lets return undefined
    return undefined;
  } else {
    //so there are arguments provided to the function
    if (args.length >= 2) {
      //there are two or more arguments provided to the function, lets consider only the first two arguments
      if (typeof(args[0]) != "number" || typeof(args[1]) != "number") {
        //so at least one of the first two arguments is not a number, lets return undefined
        return undefined;
      } else {
        //so both of the first two arguments are numbers, lets return their sum
        return args[0] + args[1];
      }
    } else { //handling input of one argument
      if (typeof(args[0]) != "number") {
        //the only argument provided is not even a number, lets return undefined
        return undefined;
      } else {
        //lets return a function that will wait for another argument 'y' to be summed with the currently provided argument 'args[0]'
        return function(y) {
          //first lets check if the second argument newly provided is a number or not
          if (typeof(y) != "number") return undefined;
          else return args[0] + y;
        };
      }
    }
  }
}


console.log(addTogether(2, 3)); // should return 5.
console.log(addTogether(2)(3)); // should return 5.
console.log(addTogether(2, "3")); // should return undefined.
console.log(addTogether(2)([3])); // should return undefined.
Hamza Dahmoun
  • 1,204
  • 10
  • 16