Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a math operator as a parameter

Tags:

javascript

I'd like to write a function in Javascript that allows me to pass in a mathematical operator and a list of ints and for each item in that list, apply the operator to it.

Thinking of it in terms of a sum, this is what I've come up with:

function accumulate(list, operator){
    var sum = 0;
    for each(var item in list){
        sum = accumulator(sum, item);
    }
    print(sum);
}

Testing this code produces the following error:

var list = new Array();
list[0] = 1;
list[1] = 2;
list[2] = 3;
js> accumulate(list, +);
js: "<stdin>", line 9: syntax error
js: accumulate(list, +);
js: ..................^
js: "<stdin>", line 9: Compilation produced 1 syntax errors.
like image 857
Talen Kylon Avatar asked Mar 10 '14 15:03

Talen Kylon


6 Answers

You can't pass an operator as a parameter, but you can pass a function:

function accumulate(list, accumulator){   // renamed parameter
    var sum = 0;
    for(var i = 0; i < list.length; i++){ // removed deprecated for…each loop
        sum = accumulator(sum, list[i]);
    }
    print(sum);
}

accumulate(list, function(a, b) { return a + b; });

This is pretty close to what the Array.prototype.reduce function does, though not exactly. To mimic the behavior of reduce, you'd have to get the first element from list and use that as the seed for your accumulator, rather than always using 0:

function accumulate(list, accumulator, seed){
    var i = 0, len = list.length;
    var acc = arguments.length > 2 ? seed : list[i++];
    for(; i < len; i++){
        acc = accumulator(acc, list[i]);
    }
    print(acc);
}

This way, you could compute the product of list (your method would always return 0):

accumulate(list, function(a, b) { return a * b; });

Update: If you're developing for newer browsers that support ECMAScript 2015 / ES6 (or using a transpiler like Babel), you can also use 'arrow function' syntax to make your code a bit more compact:

accumulate(list, (a, b) => a * b);
like image 86
p.s.w.g Avatar answered Nov 12 '22 22:11

p.s.w.g


If all the operations you are planning to do are binary operations, then you can do this

var operations = {
    "+" : function (operand1, operand2) {
        return operand1 + operand2;
    },
    "-" : function (operand1, operand2) {
        return operand1 - operand2;
    },
    "*" : function (operand1, operand2) {
        return operand1 * operand2;
    }
};

function accumulate(list, operator) {
    return list.reduce(operations[operator]);
}

console.log(accumulate([1, 2, 3, 4], "+"));     // 10
console.log(accumulate([1, 2, 3, 4], "-"));     // -8
console.log(accumulate([1, 2, 3, 4], "*"));     // 24
like image 27
thefourtheye Avatar answered Nov 12 '22 22:11

thefourtheye


I think you can do that in several different ways, but I would suggest you something like this:

var operatorFunction = {
    '+' : function(x, y) {
        return x + y;
    },
    '-' : function(x, y) {
        return x - y;
    },
    '*' : function(x, y) {
        return x * y;
    }
};

function accumul(list, neutral, operator) {
    var sum = neutral;
    list.forEach(function(item) {
        sum = operatorFunction[operator](sum, item);
    });
    return sum;
}

console.log(accumul([2, 3, 4], 0, '+'));
console.log(accumul([2, 3, 4], 0, '-'));
console.log(accumul([2, 3, 4], 1, '*'));
console.log(accumul([], 0, '+'));
console.log(accumul([], 1, '*'));

In the example above, you just need something like accumul([2, 3, 4], 0, '+'); to call you function. operatorFunction[operator] calls the correspondent operator function.

Running the example in the command line, with node.js, gives:

$ node accumulate.js 
9
-9
24
0
1

This version also work if the array is empty. You can not use list.reduce if the list is empty.

like image 7
jgrocha Avatar answered Nov 12 '22 21:11

jgrocha


I know this is an old question. Just adding some more information.

If you often use operators and need to reduce the results (accumulate), it is highly recommended to develop different helpers, so you can quickly use any input form to obtain the results.

Although, this will not be always the case when you use reduce, the following helper will allow to pass the first element of your array as default value:

reducer = (list, func) => list.slice(1).reduce(func, list.slice(0, 1).pop())

The above, still has a function dependency, so you still need to declare the specific function that wraps your target operator:

sum = list => reducer(list, (a, b) => a + b)
sum([1, 2, 3, 4, 5])

You could then redefine sum, for example, as per new input formats you see will be backwards compatible. In this example by using a new helper, flat (still experimental as per now; added the code):

flat = (e) => Array.isArray(e) ? [].concat.apply([], e.map(flat)) : e

sum = (...list)  => reducer(flat(list), (a, b) => a + b)
mult = (...list) => reducer(flat(list), (a, b) => a * b)

sum([1, 2, 3, 4, 5])
sum(1, 2, 3, 4, 5)

mult([1, 2, 3, 4, 5])
mult(1, 2, 3, 4, 5)

Then you can use reducer (or any variant you may find more useful) to simplify the definition of other helpers as well. Just one last example with matrix custom operators (in this case, they are functions):

zip  = (...lists) => lists[0].map((_l, i) => lists.map(list => list[i]))
dot_product = (a, b) => sum(zip(a, b).map(x => mult(x)))

mx_transpose = (mx) => zip.apply([], mx)
// the operator
mx_product = (m1, m2) =>
    m1.map(row => mx_transpose(m2).map(
         col => dot_product(row, col) ))
// the reducer
mx_multiply = (...mxs) => reducer(mxs, (done, mx) => mx_product(done, mx))

A = [[2, 3, 4],
     [1, 0, 0]]
B = [[0, 1000],
     [1,  100],
     [0,   10]]
C = [[2,   0],
     [0, 0.1]]

JSON.stringify(AB   = mx_product (A,  B))
JSON.stringify(ABC  = mx_product (AB, C))
JSON.stringify(ABC2 = mx_multiply(A, B, C))
like image 3
rellampec Avatar answered Nov 12 '22 22:11

rellampec


just pass 1 or -1 as input then multiply all items with this after wh.

like image 2
Jan Kleine Deters Avatar answered Nov 12 '22 20:11

Jan Kleine Deters


var list = { a: 3, b: 7, c: 5 }
var expression = (a) => a * 2;

function computeObject(list, bc){
    for(var x in list){
        list[x] = bc(list[x]);
        console.log(list[x]);
    }
}
computeObject(list, expression);
like image 2
Akash Amin Avatar answered Nov 12 '22 22:11

Akash Amin