Is possible to reduce the complexity and spaghetti quality of this Javascript algorithm solution?

自闭症网瘾萝莉.ら 提交于 2019-12-12 15:27:30

问题


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);

回答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).




回答2:


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());



回答3:


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));



回答4:


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;
}



回答5:


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!




回答6:


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]);


来源:https://stackoverflow.com/questions/37835934/is-possible-to-reduce-the-complexity-and-spaghetti-quality-of-this-javascript-al

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!