11

What is the best way to get the maximum and minimum values from a JavaScript array of objects?

Given:

var a = [{x:1,y:0},{x:-1,y:10},{x:12,y:20},{x:61,y:10}];
var minX = Infinity, maxX = -Infinity;
for( var x in a ){
  if( minX > a[x].x )
     minX = a[x].x;
  if( maxX < a[x].x )
     maxX = a[x].x;
}

Seems a bit clumsy. Is there a more elegant way, perhaps using dojo?

3
  • 2
    jsPerf test with some of the algorithms below Commented Sep 14, 2013 at 6:43
  • Thanks for the jsPerf test link. That is a great resource! Commented Sep 14, 2013 at 9:34
  • Possible duplicate of Compare JavaScript Array of Objects to Get Min / Max Commented Jun 22, 2017 at 11:46

7 Answers 7

10

It won't be more efficient, but just for grins:

var minX = Math.min.apply(Math, a.map(function(val) { return val.x; }));
var maxX = Math.max.apply(Math, a.map(function(val) { return val.x; }));

Or if you're willing to have three lines of code:

var xVals = a.map(function(val) { return val.x; });
var minX  = Math.min.apply(Math, xVals);
var maxX  = Math.max.apply(Math, xVals);
Sign up to request clarification or add additional context in comments.

5 Comments

Nice one :) I thought that Math.max/min didn't accept more than two arguments.
@wared Though, sadly, it is the slowest of the bunch :)
Sadly for me, considering your honesty, I can't say that this test is rigged :D
I now know what apply does. Thanks.
An ES6 aproach would be: const xVals = a.map((val) => val.x); const minValue = Math.min(...xVals); const maxValue = Math.max(...xVals);
6

Use this example

var lowest = Number.POSITIVE_INFINITY;
var highest = Number.NEGATIVE_INFINITY;
var tmp;
for (var i=myArray.length-1; i>=0; i--) {
    tmp = myArray[i].Cost;
    if (tmp < lowest) lowest = tmp;
    if (tmp > highest) highest = tmp;
}
console.log(highest, lowest);

5 Comments

Not only is this identical to the question in principle, but it is actually less efficient. No need for that extra variable.
Are you talking about tmp? It is MORE efficient, because it is avoids repeated lookups. There's absolutely NEED for this variable.
@Brad - unless I got the jsPerf test case wrong, this is actually quite a bit faster than even the version of the OP's algorithm that uses array indexes rather than a for/in loop.
Ah, I understand now! I was not paying attention at all. I apologize for the downvote.
You can still make this like 35% faster by using constant integers instead of mixing integers and doubles, and by looping forwards jsperf.com/creative-min-max/5. Note that this jsperf is broken in general, all the code should be extracted into non-repeated functions and those functions warmed up properly before starting timing.
4

You could use sort. This method modifies the original array, so you might need to clone it :

var b = [].concat(a); // clones "a"
b.sort(function (a, b) { return a.x - b.x; });
var min = b[0];
var max = b[b.length - 1];

4 Comments

Less efficiency. sort() is nlogn, which is slower than O(n) as OP posted.
@sza in this case, it appears the for-in loop on the array that the OP was using is slower than the sort method which surprised me as well.
@dc5 Because of the number of the elements. The performance is generally based on how the efficiency is deteriorated as the number of elements increases. Try it with 1000+ objects.
@sza - I did and I understand the numbers, I should have been more clear that what surprised me was that even with the overhead of the copy and callbacks in the sort, it was still faster even at the smaller size.
2

I know its a little too late, but for newer users you could use lodash. It makes the stuff a lot simpler.

var a = [{x:1,y:0},{x:-1,y:10},{x:12,y:20},{x:61,y:10}];

var X = [];
var Y = [];
a.map(function (val) {
    X.push(val.x);
    Y.push(val.y);
});

var minX = _.min(X);
var minY = _.min(Y);
var maxX = _.max(X);
var maxY = _.max(Y);

Or you could use .sort() to the task as procrastinator explained.

Comments

1

Another idea is to calculate max/min by reducing the values to one value. This is exactly same as your version in terms of time complexity but a bit different way to think. (reduce() is supported since JavaScript 1.8.)

var getMax = function (field) {
    return a.reduce(function (acc, c) {
        return Math.max(c[field], acc);
    }, -Infinity);
}

var getMin = function (field) {
    return a.reduce(function (acc, c) {
        return Math.min(c[field], acc);
    }, Infinity);
}

console.log(getMax('x')) //61
console.log(getMin('x')) //-1
console.log(getMax('y')) //20
console.log(getMin('y')) //0

2 Comments

No need for the acc = part, just do return Math.max(c[field], acc);
in later js versions: var getMax = (field) => a.reduce( (acc, c) => Math.max(c[field], acc), 0)
1

In case anyone comes across this in 2024, I like this method:

var a = [{ x: 1, y: 0 }, { x: -1, y: 10 }, { x: 12, y: 20 }, { x: 61, y: 10 }]

const minX = a.reduce((prev, curr) => {
  return curr.x < prev.x ? curr : prev
})

const maxX = a.reduce((prev, curr) => {
  return curr.x > prev.x ? curr : prev
})

console.log('[', minX, ',', maxX, ']')

Comments

0

You can use map functionality, but it is pretty much just a syntactic sugar around for. Any solution using reduce would be twice as slow as your "naive" because it would iterate array once for min value and once more for max. Your current solution is pretty much the best you can have in terms of performance. All you can do is to shave off some more lookups by caching them.

2 Comments

A variation (for more apples to apples) of the reduce version by @sza is actually slightly faster than the OP's for/in loop: jsPerf results
@user123444555621 The OP did mention "elegant" but also tagged the question [performance].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.