Find the min/max element of an Array in JavaScript

Find the min/max element of an Array in JavaScript

How can I easily obtain the min or max element of a JavaScript Array?
Example Psuedocode:
let array = [100, 0, 50]

array.min() //=> 0
array.max() //=> 100

Solutions/Answers:

Solution 1:

How about augmenting the built-in Array object to use Math.max/Math.min instead:

Array.prototype.max = function() {
  return Math.max.apply(null, this);
};

Array.prototype.min = function() {
  return Math.min.apply(null, this);
};

Here is a JSFiddle.

Augmenting the built-ins can cause collisions with other libraries (some see), so you may be more comfortable with just apply‘ing Math.xxx() to your array directly:

var min = Math.min.apply(null, arr),
    max = Math.max.apply(null, arr);

Alternately, assuming your browser supports ECMAScript 6, you can use the spread operator which functions similarly to the apply method:

var min = Math.min( ...arr ),
    max = Math.max( ...arr );

Solution 2:

var max_of_array = Math.max.apply(Math, array);

For a full discussion see:
http://aaroncrane.co.uk/2008/11/javascript_max_api/

Solution 3:

For big arrays (~10⁷ elements), Math.min and Math.max both produces the following error in Node.js.

RangeError: Maximum call stack size exceeded

A more robust solution is to not add every element to the call stack, but to instead pass an array:

function arrayMin(arr) {
  return arr.reduce(function (p, v) {
    return ( p < v ? p : v );
  });
}

function arrayMax(arr) {
  return arr.reduce(function (p, v) {
    return ( p > v ? p : v );
  });
}

If you are concerned about speed, the following code is ~3 times faster then Math.max.apply is on my computer. See http://jsperf.com/min-and-max-in-array/2.

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (arr[len] < min) {
      min = arr[len];
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (arr[len] > max) {
      max = arr[len];
    }
  }
  return max;
};

If your arrays contains strings instead of numbers, you also need to coerce them into numbers. The below code does that, but it slows the code down ~10 times on my machine. See http://jsperf.com/min-and-max-in-array/3.

function arrayMin(arr) {
  var len = arr.length, min = Infinity;
  while (len--) {
    if (Number(arr[len]) < min) {
      min = Number(arr[len]);
    }
  }
  return min;
};

function arrayMax(arr) {
  var len = arr.length, max = -Infinity;
  while (len--) {
    if (Number(arr[len]) > max) {
      max = Number(arr[len]);
    }
  }
  return max;
};

Solution 4:

Using spread operator (ES6)

Math.max(...array);  // the same with "min" => Math.min(...array);
const array = [10, 2, 33, 4, 5];

console.log(
  Math.max(...array)
)

Solution 5:

tl;dr

// For regular arrays:
var max = Math.max(...arrayOfNumbers);

// For arrays with tens of thousands of items:
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
  if (testArray[i] > max) {
    max = testArray[i];
  }
}

MDN solution

The official MDN docs on Math.max() already covers this issue:

The following function uses Function.prototype.apply() to find the maximum element in a numeric array. getMaxOfArray([1, 2, 3]) is equivalent to Math.max(1, 2, 3), but you can use getMaxOfArray() on programmatically constructed arrays of any size.

function getMaxOfArray(numArray) {
    return Math.max.apply(null, numArray);
}

Or with the new spread operator, getting the maximum of an array becomes a lot easier.

var arr = [1, 2, 3];
var max = Math.max(...arr);

Maximum size of an array

According to MDN the apply and spread solutions had a limitation of 65536 that came from the limit of the maximum number of arguments:

But beware: in using apply this way, you run the risk of exceeding the JavaScript engine’s argument length limit. The consequences of applying a function with too many arguments (think more than tens of thousands of arguments) vary across engines (JavaScriptCore has hard-coded argument limit of 65536), because the limit (indeed even the nature of any excessively-large-stack behavior) is unspecified. Some engines will throw an exception. More perniciously, others will arbitrarily limit the number of arguments actually passed to the applied function. To illustrate this latter case: if such an engine had a limit of four arguments (actual limits are of course significantly higher), it would be as if the arguments 5, 6, 2, 3 had been passed to apply in the examples above, rather than the full array.

They even provide a hybrid solution which doesn’t really have good performance compared to other solutions. See performance test below for more.

In 2019 the actual limit is the maximum size of the call stack. For modern Chromium based desktop browsers this means that when it comes to finding min/max with apply or spread, practically the maximum size for numbers only arrays is ~120000. Above this, there will be a stack overflow and the following error will be thrown:

RangeError: Maximum call stack size exceeded

With the script below (based on this blog post), by catching that error you can calculate the limit for your specific environment.

Warning! Running this script takes time and depending on the performance of your system it might slow or crash your browser/system!

let testArray = Array.from({length: 10000}, () => Math.floor(Math.random() * 2000000));
for (i = 10000; i < 1000000; ++i) {
  testArray.push(Math.floor(Math.random() * 2000000));
  try {
    Math.max.apply(null, testArray);
  } catch (e) {
    console.log(i);
    break;
  }
}

Performance on large arrays

Based on the test in EscapeNetscape‘s comment I created some benchmarks that tests 5 different methods on a random number only array with 100000 items.

In 2019, the results show that the standard loop (which BTW doesn’t have the size limitation) is the fastest everywhere. apply and spread comes closely after it, then much later MDN’s hybrid solution then reduce as the slowest.

Almost all tests gave the same results, except for one where spread somewhy ended up being the slowest.

If you step up your array to have 1 million items, things start to break and you are left with the standard loop as a fast solution and reduce as a slower.

JSPerf benchmark

jsperf.com benchmark results for different solutions to find the min/max item of an array

JSBen benchmark

jsben.com benchmark results for different solutions to find the min/max item of an array

JSBench.me benchmark

jsbench.me benchmark results for different solutions to find the min/max item of an array

Benchmark source code

var testArrayLength = 100000
var testArray = Array.from({length: testArrayLength}, () => Math.floor(Math.random() * 2000000));

// ES6 spread
Math.min(...testArray);
Math.max(...testArray);

// reduce
testArray.reduce(function(a, b) {
  return Math.max(a, b);
});
testArray.reduce(function(a, b) {
  return Math.min(a, b);
});

// apply
Math.min.apply(Math, testArray);
Math.max.apply(Math, testArray);

// standard loop
let max = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
  if (testArray[i] > max) {
    max = testArray[i];
  }
}

let min = testArray[0];
for (let i = 1; i < testArrayLength; ++i) {
  if (testArray[i] < min) {
    min = testArray[i];
  }
}

// MDN hibrid soltuion
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions
function minOfArray(arr) {
  var min = Infinity;
  var QUANTUM = 32768;

  for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
    var submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
    min = Math.min(submin, min);
  }

  return min;
}

minOfArray(testArray);

function maxOfArray(arr) {
  var max = -Infinity;
  var QUANTUM = 32768;

  for (var i = 0, len = arr.length; i < len; i += QUANTUM) {
    var submax = Math.max.apply(null, arr.slice(i, Math.max(i + QUANTUM, len)));
    max = Math.max(submax, max);
  }

  return max;
}

maxOfArray(testArray);

Solution 6:

If you’re paranoid like me about using Math.max.apply (which could cause errors when given large arrays according to MDN), try this:

function arrayMax(array) {
  return array.reduce(function(a, b) {
    return Math.max(a, b);
  });
}

function arrayMin(array) {
  return array.reduce(function(a, b) {
    return Math.min(a, b);
  });
}

Or, in ES6:

function arrayMax(array) {
  return array.reduce((a, b) => Math.max(a, b));
}

function arrayMin(array) {
  return array.reduce((a, b) => Math.min(a, b));
}

The anonymous functions are unfortunately necessary (instead of using Math.max.bind(Math) because reduce doesn’t just pass a and b to its function, but also i and a reference to the array itself, so we have to ensure we don’t try to call max on those as well.