3

The "JavaScriptonic" way to calculate the maximum value of an array is:

Math.max.apply(null, array)

However, this errors with "maximum call stack size exceeded" on arrays of size 2^16 (Chrome, Safari) or 2^18 (Firefox). See https://jsfiddle.net/dxcot206/

How can I use this technique safely? Is there a largest array length for which this technique is guaranteed to work?

Might the answer be different in a WebWorker, since background threads often have smaller stack sizes?

Community
  • 1
  • 1
ridiculous_fish
  • 17,273
  • 1
  • 54
  • 61

2 Answers2

5

How can I use this technique safely?

Not at all.

Is there a largest array length for which this technique is guaranteed to work?

No, there are no requirements for such things in the spec, all you are seeing are implementation-dependent "we don't support unreasonably large argument lists above N" restrictions.

The "JavaScriptonic" way to calculate the maximum value of an array is: Math.max.apply(null, array)

It's just short, but not very efficient, and as you have seen might not work. Better use array.reduce((a, b) => Math.max(a, b)).

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Is reduce applicable to _unreasonably large array_? – Morteza Tourani Jun 25 '16 at 22:46
  • 1
    @mortezaT: Yes, it is (otherwise I wouldn't have suggested it). Btw, an unreasonably lengthy `arguments` list might not be an unreasonably large array :-) – Bergi Jun 25 '16 at 22:50
  • I would do it like `array.reduce((a,b) => a <= b && b || a);` and just because how it makes `a` look so sexy. – Redu Jun 25 '16 at 22:51
  • 1
    @Redu your `max` doesn't work if `0` was the largest element in an array full of negative numbers otherwise. – BeyelerStudios Jun 25 '16 at 22:57
  • @BeyelerStudios Oh..! 0 is always a problem when you have &&s ans ||s around... gotto be cautious. Good point. – Redu Jun 25 '16 at 22:59
  • 1
    Thanks for your answer. It's distressing that ES6 would further cement this practice with the new spread operator. – ridiculous_fish Jun 26 '16 at 01:24
  • @Redu: Just do `array.reduce((a, b) => a < b ? b : a)` instead of weird short-circuiting – Bergi Jun 26 '16 at 17:38
0

Is there a largest array length for which this technique is guaranteed to work? Might the answer be different in a WebWorker, since background threads often have smaller stack sizes?

To help answer your second (2) questions in a particular environment, here's a script you can run that uses a binary search to figure out the answer:

(function () {
  // Returns whether or not the action threw an exception.
  function throwsException(action) {
    try {
      action();
    } catch (e) {
      return true;
    }
    return false;
  }

  // Performs the action for various values between lower and upper and returns
  // the maximum value for the action to return true.
  function findMaxValueForAction(action, lower, upper) {
    var best;

    while (upper - lower > 1) {
      var previousUpper = upper;

      var guess = Math.floor((lower + upper) / 2);
      if (action(guess)) {
        // If the action is successful then the lower needs to be updated to what just succeeded. 
        lower = guess;
        // Is the (successful) lower better than the current best?
        if (best === undefined || lower > best) {
          // If so update the current best.
          best = lower;
        }
      } else {
        // If the action was unsuccessful the new upper bound is 1 less than what we just tried.
        upper = guess - 1;
      }
    }

    return best;
  }

  var maxArraySize = findMaxValueForAction(function (value) {    
    return !throwsException(function () {
      var array = new Array(value);
    });
  }, 0, Number.MAX_SAFE_INTEGER);

  var maxArrayApplySize = findMaxValueForAction(function (value) {
    return !throwsException(function () {
      var array = new Array(value);
      Math.max.apply(null, array);
    });
  }, 0, maxArraySize);

  return [
    'Max "value" for "new Array(value)": ' + maxArraySize, 
    'Max "value" for "Math.max.apply(null, new Array(value))": ' + maxArrayApplySize
  ];
})().forEach(function(message) { console.log(message) });
Jesus is Lord
  • 14,971
  • 11
  • 66
  • 97