I realized the answer as I was posting my own question: This is the most succinct way of taking the min of an array x in JavaScript. The first argument is totally arbitrary; I find the 0 confusing because the code intuitively means "Take the min of 0 and x," which is absolutely not the case. Using the Math object makes more sense for human-readability, but the Raphael.js authors are obsessed with minification and 0 is three bytes shorter.
See http://ejohn.org/blog/fast-javascript-maxmin/
For readability's sake, I'd strongly urge people to stop doing this and instead define a function along the lines of
function arrayMin(arr) { return Math.min.apply(Math, arr); };
Answer from Trevor Burnham on Stack OverflowI realized the answer as I was posting my own question: This is the most succinct way of taking the min of an array x in JavaScript. The first argument is totally arbitrary; I find the 0 confusing because the code intuitively means "Take the min of 0 and x," which is absolutely not the case. Using the Math object makes more sense for human-readability, but the Raphael.js authors are obsessed with minification and 0 is three bytes shorter.
See http://ejohn.org/blog/fast-javascript-maxmin/
For readability's sake, I'd strongly urge people to stop doing this and instead define a function along the lines of
function arrayMin(arr) { return Math.min.apply(Math, arr); };
The reason is this:
Your input
xis an arrayThe signature of
Math.min()doesn't take arrays, only comma separated argumentsIf you were using
Function.prototype.call()it would have almost the same signature, except the first argument is thethiscontext, i.e. who's "calling"- Example:
Math.min.call(context, num1, num2, num3)
- Example:
The
contextonly matters when you refer tothisinside the function, e.g. most methods you can call on an array:Array.prototype.<method>would refer tothis(the array to the left of the 'dot') inside the method.Function.prototype.apply()is very similar to.call(), only that instead of taking comma-separated arguments, it now takes an array after thecontext.Function.prototype.call(context, arg1, arg2, arg3)Function.prototype.apply(context, [arg1, arg2, arg3])
The
0ornullthat you put in as the first argument is just a place shifter.
Why don't Math.max and Math.min accept arrays? Why are they n-arity functions?
How do Javascript Math.max and Math.min actually work? - Stack Overflow
javascript - Why does this code excert have to use Math.min.apply rather than just Math.min - Stack Overflow
Find the min/max element of an array in JavaScript - Stack Overflow
Videos
Edit: I am not asking for a way to use these methods with arrays. What I am asking is why in the first place do they not accept arrays.
However, I will list the solutions mentioned:
-
Math.max.apply(null, arr) -
Math.max(...arr) -
arr.reduce((max, current) => (current > max ? current : max), -Infinity);
Here is the Math.max code in Chrome V8 engine.
function MathMax(arg1, arg2) { // length == 2
var length = %_ArgumentsLength();
if (length == 2) {
arg1 = TO_NUMBER(arg1);
arg2 = TO_NUMBER(arg2);
if (arg2 > arg1) return arg2;
if (arg1 > arg2) return arg1;
if (arg1 == arg2) {
// Make sure -0 is considered less than +0.
return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
}
// All comparisons failed, one of the arguments must be NaN.
return NaN;
}
var r = -INFINITY;
for (var i = 0; i < length; i++) {
var n = %_Arguments(i);
n = TO_NUMBER(n);
// Make sure +0 is considered greater than -0.
if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
r = n;
}
}
return r;
}
Here is the repository.
Below is how to implement the functions if Math.min() and Math.max() did not exist.
Functions have an arguments object, which you can iterate through to get its values.
It's important to note that Math.min() with no arguments returns Infinity, and Math.max() with no arguments returns -Infinity.
function min() {
var result= Infinity;
for(var i in arguments) {
if(arguments[i] < result) {
result = arguments[i];
}
}
return result;
}
function max() {
var result= -Infinity;
for(var i in arguments) {
if(arguments[i] > result) {
result = arguments[i];
}
}
return result;
}
//Tests
console.log(min(5,3,-2,4,14)); //-2
console.log(Math.min(5,3,-2,4,14)); //-2
console.log(max(5,3,-2,4,14)); //14
console.log(Math.max(5,3,-2,4,14)); //14
console.log(min()); //Infinity
console.log(Math.min()); //Infinity
console.log(max()); //-Infinity
console.log(Math.max()); //-Infinity
It's because the Function.prototype.apply() method has two parameters. First is the newly assigned this value and the second is an array of arguments that will be passed to the called function.
Your example does not do anything special with the first parameter but it does with the second.
Math.min() accepts an infinite amount of arguments. Your examples passes the array of dates as an argument and spreads the items in the array as arguments. Nowadays you could do something like this, with the spread syntax.
var minDate = new Date(Math.min(...dates));
Math.min requires one or more values passed as parameters. Since the date strings have been pushed into an array, passing an array will just use the array as a value, not the dates.
Using apply will pass the elements of the array as arguments, it's effectively the same as using spread syntax:
Math.min(...dates);
Since the first argument passed to apply is the value to use for this, and Math.apply doesn't care what this is, null is passed (you could pass any value).
This technique was common before spread syntax was introduced in ECMAScript 2016 (ed 7).
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);
};
let p = [35,2,65,7,8,9,12,121,33,99];
console.log(`Max value is: ${p.max()}` +
`\nMin value is: ${p.min()}`);
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 spread syntax which functions similarly to the apply method:
var min = Math.min( ...arr ),
max = Math.max( ...arr );
var max_of_array = Math.max.apply(Math, array);
For a full discussion see: http://aaroncrane.co.uk/2008/11/javascript_max_api/