Javascript function challenge add (1,2) and add (1) (2) both should return 3
A friend of mine asked me to write a function that works with both of these scripts
add(2,4) // 6
add(2)(4) // 6
My instinct was to write an add () function that returns itself, but I'm not sure if I'm heading in the right direction. It failed.
function add(num1, num2){
if (num1 && num2){
return num1 + num2;
} else {
return this;
}
}
alert(add(1)(2));
So I started reading about functions that return other functions or return themselves.
- http://davidwalsh.name/javascript-functions
- JavaScript: an auto-call function returns a closure. What is this for?
- JavaScript: an auto-call function returns a closure. What is this for?
I'll keep trying, but if anyone has a great solution, I'd love to see it!
source to share
Dr. Dobs has an article on Currying and Partial Functions in JavaScript "that describes exactly this problem.
One of the solutions found in this article:
// 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;
}
source to share
I wrote a function that generates the chain, function valueOf
and context this
) are constantly updated with a new sum, no matter how many arguments are passed each time.
/* add function */
function add() {
"use strict";
var args, sum, chain;
args = Array.prototype.slice.call(arguments);
sum = typeof this === 'number' ? this : 0;
sum += args.reduce(function (p, n) { return p + n; }, 0);
chain = add.bind(sum);
chain.valueOf = function () {
return sum;
};
return chain;
}
/* 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 */
var add7 = add(7);
console.log('var add7 = add(7)');
console.log('add7(3) = ' + add7(3));
console.log('add7(8) = ' + add7(8));
The reason both mechanisms are required is because the next call in the chain cannot access the binding function valueOf
, and any script trying to evaluate the function as a number cannot access its context.
The only drawback is the requirement strict mode
to this
remain primitive.
Edited here to support both strict mode
non-strict mode:
function add() {
var args, sum, chain;
args = Array.prototype.slice.call(arguments);
// Number object from non-strict mode
if (this instanceof Number) {
sum = Number(this);
// number primitive from strict mode
} else if (typeof this === 'number') {
sum = this;
// initial call to add
} else {
sum = 0;
}
sum += args.reduce(function (p, n) { return p + n; }, 0);
chain = add.bind(sum);
chain.valueOf = function () {
return sum;
};
return chain;
}
source to share
The concept you are looking for is called currying and it has to do with function conversion and partial function application. This is useful when you call the same function over and over again with the same arguments.
An example implementation add(2)(6)
via currying will look something like this:
function add(x,y) {
if (typeof y === 'undefined') {
return function(y) {
return x + y;
}
}
}
add(2)(4); // => 6
Alternatively, you can do something like this ...
var add6 = add(6);
typeof add6; // => 'function'
add6(4); // => 10
source to share
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));
Advanced solution depending on 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));
source to share
I decided to add another solution for fun using some ES6 features:
/* add function */
const add = (function add () {
const sum = [this, ...arguments].reduce((a, b) => a + b)
const chain = add.bind(sum)
chain[Symbol.toPrimitive] = hint => 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 */
var add7 = add(7)
console.log('var add7 = add(7)')
console.log('add7(3) = ${add7(3)}')
console.log('add7(8) = ${add7(8)}')
source to share