If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

Enjoy an ad free experience by logging in. Not a member yet? Register.

Maximum value in an array

I have noticed a number of queries where someone needs to get the maximum (or sometimes the minimum) value that is contained in an array. Just about every solution offered involves multiple lines of code and a loop but there is actually a single JavaScript command that will retrieve the maximum value from an array directly with only the one statement and no loop being required.

Let's start with an array of numbers where we need to get the maximum value.

Code:

ary = [4,7,-2,55,2,12];

It doesn't really matter how we get those numbers into the array, all that matters is that we have an array and want to get the maximum value. To get the maximum value from the array we run the following code:

Code:

max = Math.max.apply(Math,ary);

The variable max now contains the maximum value from the array called ary (if you run both these statements then max contains 55).

To get the minimum value just substitute Math.min instead of Math.max

No loops and just a single call and we have the answer.

Where JavaScript provides a single call that gives the answer you should not try to reinvent the wheel by writing your own less efficient code to do the same thing. There are enough things that JavaScript doesn't provide single commands for that can be used instead in homework questions where a loop would be required (or whatever the question is intended to test) without asking questions that are better solved wothout using the construct that the question is supposed to be testing.

if you need to support browsers going back a couple versions behind the current, a drop-in replacement will provide enough .bind() for this pattern to work.

since we're here, i'll mention my other favorite bind, a slightly more complex snip that makes a callable that allows you create arrays from anything with a .length property, like String, domCollections, arguments, etc:

you can re-bind getElementsByTagName, querySelectorAll, and all those other long-winded dom methods into short callable functions.

since the JS compiler has less of an activation envelope to create, a bound function runs 10-20% faster than an unbound one. this can almost erase the typical function call overhead of 18%, and your user functions say "[native code]" when you stringify them; very cool.

@felgall - sorry about the hijacking. i was just going to mention .bind() and be on my way, but i obviously got carried away again. it happens, and i think this is good info and related to your idea, so i won't delete it.

@rnd_me: could you explain why var max=Math.max.apply.bind(Math.max, 0); requires a second parameter? (I tried various ones, it only matters that there is one)

PS. John Resig claims responsibility for that "hack" to the Book "JavaScript, the definitive guide".

The computer is always right. The computer is always right. The computer is always right. Take it from someone who has programmed for over ten years: not once has the computational mechanism of the machine malfunctioned.

@rnd_me: could you explain why var max=Math.max.apply.bind(Math.max, 0); requires a second parameter? (I tried various ones, it only matters that there is one)
.

a lot of folks, myself included, are confused at first by bind.

my big "aha" moment of understanding came by lining it up with the other two Function.prototype methods we all use and love; call() and apply().

turns out, bind() is EXACTLY like call() : it sets "this" with the first argument, and passes all other specified arguments as passed to the function you are calling it on. the only diff is that call() executes the function right there, whereas bind() stops short, returning the scoped function (the same as call would use) instead of the result.

let's throw bind() out, and examine the issue with the 1999 pieces.

you may have done something like this at some point:

Code:

Math.max.apply("something", [1,2,3,4])

you need to use "something" there for the same reason as zero (or something else) in my code.

apply's first argument is sucked into this.
if all that was passed was the array, max() would be called with the array as this, and with no arguments. apply() wants an array of arguments as arguments[1] to turn into arguments on the applied function.

the reason this works is because Math.max() doesn't look at this, it only looks at it's arguments, one number per argument:

less obvious in my code is what's being bound.
it's not max(), it's apply().

check this out:

Code:

max=String.apply.bind(Math.max, 0);
max([1,2,3]) // ===3

yeah, that's weird, but it works because apply() doesn't care what is in front or behind it. it's a generic. i guess you can think of it as being "loose"; it doesn't care what function, this, or arguments it's hooked up with. heh.

hmm, that a bit of typing i just did, doh!
im tryin to stop my mic-hogging.
you can "alert-ify" the Function.prototype.bind replacement on the MDC page i linked to if you want to step-through bind() like the apply2 i made above.

less obvious in my code is what's being bound.
it's not max(), it's apply().

check this out:

Code:

max=String.apply.bind(Math.max, 0);
max([1,2,3]) // ===3

yeah, that's weird, but it works because apply() doesn't care what is in front or behind it. it's a generic. i guess you can think of it as being "loose"; it doesn't care what function, this, or arguments it's hooked up with. heh.

it made "click" here. what I didn’t see is that the Math.max in front of apply.bind() is just the source for apply() and hence the first parameter is the this for apply() and the second the this for bind() (which is irrelevant as there is no this in a static function).

The computer is always right. The computer is always right. The computer is always right. Take it from someone who has programmed for over ten years: not once has the computational mechanism of the machine malfunctioned.

@felgall - sorry about the hijacking. i was just going to mention .bind() and be on my way, but i obviously got carried away again. it happens, and i think this is good info and related to your idea, so i won't delete it.

I don't see how you hijacked the thread with your suggestion. As you said it is good info and definitely related to what I posted.

The part you didn't cover is why call and apply (and bind) have that first parameter. The original intention for those Function methods is to allow a method attached to one object to be borrowed by a different object. The first parameter is the object that is borrowing the method from the one that is being called in the code. It just happens that in each of the examples that we have posted here that we are borrowing the method to use as a function rather than as a method attached to a different object. Since the code we are calling makes no reference to "this" it doesn't care what is in that first parameter. Where the function being called - max() in my example - doesn't contain any references to the "this" it doesn't matter what you place in the first parameter as the object that the non-existent "this" references are to refer to. The advantage apply() has in situations like in my original post is that it takes care of converting an array into an arguments list for you in the same way that var args = Array.prototype.slice.call(arguments); converts arguments into an array. (you can of course shorten both ways using bind).

You can also use apply when you have nested functions and want to pass the arguments passed to the outer function directly into the inner function.

Example 8.5 on page 189 of the book "JavaScript: The Definitive Guide" 6th Edition has code to add Function.prototype.bind for those browsers that don't support it natively. There are a couple of minor differences that the text on the page lists but the supplied code ought to allow most of the uses for bind() that you can think of in the older browsers - so there is no reason to avoid using it just because some of your visitors are using those older browsers.

Just one extra point - If you use 0 as the second parameter in the bind for Math.max.apply then you eliminate the possibility of the maximum being negative. That second parameter should be Number.NEGATIVE_INFINITY in order to allow the maximum to be negative.

Just one extra point - If you use 0 as the second parameter in the bind for Math.max.apply then you eliminate the possibility of the maximum being negative. That second parameter should be Number.NEGATIVE_INFINITY in order to allow the maximum to be negative.

aw man, you shoulda stopped while you're ahead; you got the ball rollin on this. i did cover the 2nd arg implications in my follow-up to Dormilich's note. or, i should say, i tried...

but, for the sake of accurate info for noobs reading this thread, i must point out that you are dead wrong about this particular point.

the 2nd param to bind (in the case of Math.max) is ignored completely, so negatives work using 0 as well as false, {}, or [/rx/]; anything goes except nothing.

-2 is the largest numeral in the set, so this code immediately falsifies your hypothesis that the use of "0 as the second parameter in the bind for Math.max.apply ... eliminate(s) the possibility of the maximum being negative" (emphasis mine).

try something less confusing; using garbage for this, and a different apply:

aw man, you shoulda stopped while you're ahead; you got the ball rollin on this. i did cover the 2nd arg implications in my follow-up to Dormilich's note. or, i should say, i tried...

but, for the sake of accurate info for noobs reading this thread, i must point out that you are dead wrong about this particular point.

Strange because I tested it prior to adding that comment to the end of my post and only added the comment because in the browser I was testing in the value that parameter did make a difference.

The test I did was with my original array with the - sign removed and replacing max with min - and then with the 0 as the second parameter I got the 0 returned instead of the 2 only by changing the 0 to a number bigger than the minimum in the array could I get it to return the right answer. The first parameter to the bind substitutes for 'this' but the second parameter simply got added to the array.

Obviously the implementation of bind varies slightly between browsers. Had my test returned the same result that your's did then I would not have gone back and amended the post to add that sentence. The difference probably relates to the number of objects that the browser considers to be involved that need to have a value supplied for 'this'.