Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JS call function with all possible arguments permuted

Tags:

javascript

Consider following arrays:

var array1 = [true, false];
var array2 = [1, 2];
var array3 = ["a", "b", "c"];

I want to call my function myFunc(arg1, arg2, arg3) with all argument combinations. But I want to avoid to "foreach" hell.

Is it possible write function that allows me that, so i can call it some like:

cartesianCall(array1, array2, array3, myFunc);

ideally with variable count of arrays (myFunc arguments)?

EDIT: so function would be called:

myFunc(true, 1, "a");
myFunc(true, 1, "b");
myFunc(true, 1, "c");
myFunc(true, 2, "a");
myFunc(true, 2, "b");
myFunc(true, 2, "c");
myFunc(false, 1, "a");
myFunc(false, 1, "b");
myFunc(false, 1, "c");
myFunc(false, 2, "a");
myFunc(false, 2, "b");
myFunc(false, 2, "c");
like image 470
the.ufon Avatar asked Jul 30 '13 18:07

the.ufon


2 Answers

Declare you function without parameters and use arguments keyword:

function cartesianCall() {
  for (var i = 0; i < arguments.length; i++) {
     // do something with arguments[i]
  }
}
like image 147
Yuriy Galanter Avatar answered Oct 31 '22 22:10

Yuriy Galanter


http://jsfiddle.net/trevordixon/zEqKy/

function cartesianCall(func, args) {
    var combos = allCombos.apply(this, args);

    for (var i = 0; i < combos.length; i++) {
        func.apply(null, combos[i]);
    }
}

function allCombos(first) {
    var isArray = toString.call(first) === "[object Array]";
    if (!isArray) first = [first]; // Convert non-array to an array with the value
                                   // as the only element
    else if (first.length === 0) first = [undefined]; // Convert empty array to an
                                                      // array with undefined as
                                                      // the only element

    if (arguments.length === 1) return first; // base case for recursion

    var result = [],
        rest = allCombos.apply(this, Array.prototype.slice.call(arguments, 1));

    for (var i = 0; i < first.length; i++) {
        for (var j = 0; j < rest.length; j++) {
            result.push([first[i]].concat(rest[j]));
        }
    }

    return result;
}

Then use it like this:

function printArgs() { console.log('Called with arguments:', arguments); }

cartesianCall(printArgs, [
    [true, false],
    undefined,
    [1, 2],
    [],
    'a string',
    ['a', 'b', 'c']
]);

Prints:

Called with arguments: [true, undefined, 1, undefined, "a string", "a"] 
Called with arguments: [true, undefined, 1, undefined, "a string", "b"] 
Called with arguments: [true, undefined, 1, undefined, "a string", "c"] 
Called with arguments: [true, undefined, 2, undefined, "a string", "a"] 
Called with arguments: [true, undefined, 2, undefined, "a string", "b"] 
Called with arguments: [true, undefined, 2, undefined, "a string", "c"] 
Called with arguments: [false, undefined, 1, undefined, "a string", "a"] 
Called with arguments: [false, undefined, 1, undefined, "a string", "b"] 
Called with arguments: [false, undefined, 1, undefined, "a string", "c"] 
Called with arguments: [false, undefined, 2, undefined, "a string", "a"] 
Called with arguments: [false, undefined, 2, undefined, "a string", "b"] 
Called with arguments: [false, undefined, 2, undefined, "a string", "c"]

Notice that empty arrays are treated the same as undefined.

like image 21
Trevor Dixon Avatar answered Oct 31 '22 22:10

Trevor Dixon