javascriptecmascript-3

JavaScript Refactor From Two to Many Function Arguments


These two JavaScript functions each accept TWO array arguments and return ONE array result. Conforming to ES3, how can I rewrite these to accept an indefinite number of array inputs?

function sum(v, w) {
    for (var a = jsArray(v), b = jsArray(w), t = 0; t < a.length; t++) a[t] += b[t];
    return vbArray(a);
}

function mul(v, w) {
    for (var a = jsArray(v), b = jsArray(w), t = 0; t < a.length; t++) a[t] *= b[t];
    return vbArray(a);
}

The odd jsArray() function is required because the arrays to be processed are coming from VBA and jsArray() converts them to JavaScript arrays:

function jsArray(v) {
    return new VBArray(v).toArray()
}

Solution

  • You can try to use array-like object arguments in order to get all passed arguments:

    function someFunc() {
      for (var i=0; i < arguments.length; i++) {
        console.log(arguments[i]);
      }
    }
    
    someFunc('a', 'b', 'c');

    Example of transformed sum function. Beware that this works if all the passed arrays have the same length.

    function sum() {
      var arrays = [];
      
      for (var i = 0; i < arguments.length; i++) {
        arrays[i] = jsArray(arguments[i]);
      }
      
      for (var j = 1; j < arrays.length; j++) {
        for (var t = 0; t < arrays[0].length; t++) {
           arrays[0][t] += arrays[j][t];
        }
      }
      
      return vbArray(arrays[0]);
    }

    Generalized solution:

    function process(concreteFunc) {
      var arrays = [];
    
      for (var i = 1; i < arguments.length; i++) {
        arrays.push(jsArray(arguments[i]));
      }
    
      for (var j = 1; j < arrays.length; j++) {
        for (var t = 0; t < arrays[0].length; t++) {
           arrays[0][t] = concreteFunc(arrays[0][t], arrays[j][t]);
        }
      }
    
      return vbArray(arrays[0]);
    }
    
    var sum = process.bind(null, function (a, b) {return a + b});
    var mul = process.bind(null, function (a, b) {return a * b});

    Solution without .bind:

    function process(concreteFunc, args) {
      var arrays = [];
    
      for (var i = 0; i < args.length; i++) {
        arrays.push(jsArray(args[i]));
      }
    
      for (var j = 1; j < arrays.length; j++) {
        for (var t = 0; t < arrays[0].length; t++) {
           arrays[0][t] = concreteFunc(arrays[0][t], arrays[j][t]);
        }
      }
    
      return vbArray(arrays[0]);
    }
    
    function createFunc(handler) {
      return function() {
        return process(handler, Array.prototype.slice.call(arguments));
      }
    }
    
    var sum = createFunc(function (a, b) {return a + b});
    var mul = createFunc(function (a, b) {return a * b});

    Improved version to support ability to implement avg:

    function process(concreteFunc, args) {
      var arrays = [];
    
      for (var i = 0; i < args.length; i++) {
        arrays.push(jsArray(args[i]));
      }
    
      var result = [];
      
      for (var j = 0; j < arrays[0].length; j++) {
        var items = [];
        
        for (var t = 0; t < arrays.length; t++) {
          items.push(arrays[t][j]);
        }
        
        result.push(concreteFunc(items));
      }
    
      return vbArray(result);
    }
    
    function createFunc(handler) {
      return function() {
        return process(handler, Array.prototype.slice.call(arguments));
      }
    }
    
    function reduce(items, handler) {
      var result = items[0];
      for (var i = 1; i < items.length; i++) {
        result = handler(result, items[i]);
      }
      
      return result;
    }
    
    var sum = createFunc(function(items) {
      return reduce(items, function (a, b) {return a + b});
    });
    var mul = createFunc(function(items) {
      return reduce(items, function (a, b) {return a * b});
    });
    var avg = createFunc(function(items) {
      return reduce(items, function (a, b) {return a + b}) / items.length;
    });