Finding the max value of a property in an array of objects
Asked Answered
S

23

781

I'm looking for a really quick, clean and efficient way to get the max "y" value in the following JSON slice:

[
  {
    "x": "8/11/2009",
    "y": 0.026572007
  },
  {
    "x": "8/12/2009",
    "y": 0.025057454
  },
  {
    "x": "8/13/2009",
    "y": 0.024530916
  },
  {
    "x": "8/14/2009",
    "y": 0.031004457
  }
]

Is a for-loop the only way to go about it? I'm keen on somehow using Math.max.

Sillabub answered 26/10, 2010 at 4:54 Comment(0)
T
1256

To find the maximum y value of the objects in array:

Math.max.apply(Math, array.map(function(o) { return o.y; }))

or in more modern JavaScript:

Math.max(...array.map(o => o.y))

Warning:

This method is not advisable, it is better to use reduce. With a large array, Math.max will be called with a large number of arguments, which can cause stack overflow.

Thymelaeaceous answered 26/10, 2010 at 5:4 Comment(16)
Could you expand this answer to show how to return the object that the max value was found in? That would be very helpful, thanks!Freya
Here is the fiddle! hope this will help to someone jsfiddle.net/45c5r246Attest
@MikeLyons if you still care about getting the actual object: jsfiddle.net/45c5r246/34Thymelaeaceous
FWIW my understanding is when you call apply on a function it executes the function with a specified value for this and a series of arguments specified as an array. The trick is that apply turns the array into a series of actual function arguments. So in this case it finally calls Math.max(0.0265, 0.0250, 0.024, 0.031) with the this of the function executed being Math. I can't see why it should be Math frankly, I don't think the function requires a valid this. Oh and here is a proper explanation: #21255638Sardanapalus
@DanielC Math as this is just because that is what it would be if you called it as Math.max(a,b,c). I am 99% sure this is unneeded on any browser though.Thymelaeaceous
Math.max.apply(Math,Array) returns the max value for the given array. array.map(function(o){return o.y;}) returns an array of the y values from your objects. Then in the second line of the fiddle array.find(function(o){ return o.y == valueReturnedByMathMax; }) you just look for the object containing that max value.Etem
Careful to call this only when the array is not empty, otherwise the returned value will be InfinityAcquit
How do you return the object with the highest value?, like {"x":"8/11/2009","y":0.026572007}Saiga
Math.max(...array.map(o => o.y)) <3 thx to atom code formatterSoftshoe
But what if there are two object with the same value on the array? {"x":"8/11/2009","y":0.026572007}, {"x":"10/11/2009", "y": 0.026572007}. I mean the find would stop to the first occurence but what for the Math.min?Panzer
Wouldn't this iterate over all objects twice? Reduce would avoid that and give you more flexibility to return either object or value. See reduce answer (by someone else) below. Even without reduce, writing a quick function that stored current value in a local variable and iterated once and replaced value if necessary would work better (what reduce does basically)Whitton
a more concise snippet to get the max value: Math.max(...array.map(o => o.y))Necktie
@tobyodavies, thanks a lot your answer!! Its so simple and very good!! Can you put the ES6 solution too like @Amnom said please? (I had just done this too haha) Math.max( ...array.map(o=>o.y) )Semiconscious
if we use this with react native the app might get crashed if there is large amount of dataOption
This method is not advisable, it is better to use reduce. With a large array, apply will be calling max with a large number of arguments, which can cause stack overflow. Refer: #30810156Teary
@SamWatkins 's comment should be added to the answer!Filiation
C
578

Find the object whose property "Y" has the greatest value in an array of objects

One way would be to use Array reduce..

const max = data.reduce(function(prev, current) {
    return (prev && prev.y > current.y) ? prev : current
}) //returns object

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce http://caniuse.com/#search=reduce (IE9 and above)

If you don't need to support IE (only Edge), or can use a pre-compiler such as Babel you could use the more terse syntax.

const max = data.reduce((prev, current) => (prev && prev.y > current.y) ? prev : current)
Cubature answered 4/12, 2015 at 11:55 Comment(11)
This is a good answer, however, you would like to pass an initial value or you would get an error in case the data array is empty. i.e. for an autoincrement index of objects. const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current, 1)Arbutus
You raise a good point, I would probably choose null over 1.Cubature
Note that this returns the object that had the max value not the max value from the object. This may or may not be what you want. In my case it was what I wanted. +1Commentator
Excellent answer! At first, I hesitated due to the reduce, but we're gonna need to iterate anyway, so why not?Fredericton
This is better than the accepted answer, because it's unsafe to call apply with a large array, it can cause stack overflow: #30810156Teary
@AndyPolhill if you pass null instead of 1 as an initial value, then you get TypeError: Cannot read property 'y' of nullKnacker
Also, make sure the data type is a number and not a string. Otherwise you could get mixed results. Use parseInt() where appropriate.Monitory
if some object doesn't have a your comparing property that would be a problem.Circumstantial
This is the answer to the actual question in the title which says the max attribute which could be of any type. Granted the example is numbers. I also really like that this returns the object rather than the value. Very useful.Chive
I've updated the answer to guard against "prev" being null. This allows you to call reduce with a null initial value.Gross
@Gross what aobut missing or string props?Lowspirited
P
260

clean and simple ES6 (Babel)

const maxValueOfY = Math.max(...arrayToSearchIn.map(o => o.y), 0);

The second parameter should ensure a default value if arrayToSearchIn is empty.

Pinchbeck answered 13/9, 2016 at 3:43 Comment(6)
also it's good to know that it returns -Infinity (a truthy value) for an empty arraySunda
This is supported in most modern browsers without Babel now.Kaila
as it returns -Infinity for an empty array, you can pass an initial value Math.max(...state.allProjects.map(o => o.id), 1);Arbutus
to handle case for minus values change 0 to arrayToSearchIn[0].y. Time complexity comparision: https://mcmap.net/q/53908/-finding-the-max-value-of-a-property-in-an-array-of-objectsHenrietta
how to get the object here ?Marpet
This method also causes stack overflow for a large array.Teary
P
173

Comparison of three ONELINERS which handle minus numbers case (input in a array):

var maxA = a.reduce((a,b)=>a.y>b.y?a:b).y; // 30 chars time complexity:  O(n)

var maxB = a.sort((a,b)=>b.y-a.y)[0].y;    // 27 chars time complexity:  O(nlogn)
           
var maxC = Math.max(...a.map(o=>o.y));     // 26 chars time complexity: >O(2n)

Editable example. Ideas from: maxA, maxB and maxC (side effect of maxB is that array a is changed because sort is in-place).

var a = [
  {"x":"8/11/2009","y":0.026572007},{"x":"8/12/2009","y":0.025057454},    
  {"x":"8/14/2009","y":0.031004457},{"x":"8/13/2009","y":0.024530916}
]

var maxA = a.reduce((a,b)=>a.y>b.y?a:b).y;
var maxC = Math.max(...a.map(o=>o.y));
var maxB = a.sort((a,b)=>b.y-a.y)[0].y;

document.body.innerHTML=`<pre>maxA: ${maxA}\nmaxB: ${maxB}\nmaxC: ${maxC}</pre>`;

For bigger arrays the Math.max... will throw exception: Maximum call stack size exceeded (Chrome 76.0.3809, Safari 12.1.2, date 2019-09-13)

let a = Array(400*400).fill({"x": "8/11/2009", "y": 0.026572007 }); 

// Exception: Maximum call stack size exceeded

try {
  let max1= Math.max.apply(Math, a.map(o => o.y));
} catch(e) { console.error('Math.max.apply:', e.message) }

try {
  let max2= Math.max(...a.map(o=>o.y));
} catch(e) { console.error('Math.max-map:', e.message) }

Benchmark for the 4 element array

Profanity answered 6/12, 2018 at 15:11 Comment(5)
one thing to point out is option B makes it much easier to get the whole object with the max y value by leaving off the .y at the end.Polson
Im not sure to understand why maxA's Math.max() has a second parameter? It should work with only var maxA = Math.max(...a.map(o=>o.y));, wouldn't it?Maebashi
For those curious about the difference (if any) between O(n) and O(2n) see #25778214Poundage
@jHova - I not use strict big O notation - this is more "practical / intuitive" notation and meaning is following: the number of operations in O(2n) is twice as high as in O(n) (I'm not going to define it precisely)Henrietta
With my lack of math expertise, I just wanted to know which is faster without having to edit the code above with timers and sample runs. According to https://mcmap.net/q/55334/-is-n-or-nlog-n-better-than-constant-or-logarithmic-time, best bet is O(n), though actual usage could vary.Tempting
W
33

I'd like to explain the terse accepted answer step-by-step:

var objects = [{ x: 3 }, { x: 1 }, { x: 2 }];

// array.map lets you extract an array of attribute values
var xValues = objects.map(function(o) { return o.x; });
// es6
xValues = Array.from(objects, o => o.x);

// function.apply lets you expand an array argument as individual arguments
// So the following is equivalent to Math.max(3, 1, 2)
// The first argument is "this" but since Math.max doesn't need it, null is fine
var xMax = Math.max.apply(null, xValues);
// es6
xMax = Math.max(...xValues);

// Finally, to find the object that has the maximum x value (note that result is array):
var maxXObjects = objects.filter(function(o) { return o.x === xMax; });

// Altogether
xMax = Math.max.apply(null, objects.map(function(o) { return o.x; }));
var maxXObject = objects.filter(function(o) { return o.x === xMax; })[0];
// es6
xMax = Math.max(...Array.from(objects, o => o.x));
maxXObject = objects.find(o => o.x === xMax);


document.write('<p>objects: ' + JSON.stringify(objects) + '</p>');
document.write('<p>xValues: ' + JSON.stringify(xValues) + '</p>');
document.write('<p>xMax: ' + JSON.stringify(xMax) + '</p>');
document.write('<p>maxXObjects: ' + JSON.stringify(maxXObjects) + '</p>');
document.write('<p>maxXObject: ' + JSON.stringify(maxXObject) + '</p>');

Further information:

Wadleigh answered 26/9, 2016 at 12:51 Comment(1)
Great explanation! It might be a bit easier to read if it wasn't in code comments, but still - great workBlackcock
L
23

Well, first you should parse the JSON string, so that you can easily access it's members:

var arr = $.parseJSON(str);

Use the map method to extract the values:

arr = $.map(arr, function(o){ return o.y; });

Then you can use the array in the max method:

var highest = Math.max.apply(this,arr);

Or as a one-liner:

var highest = Math.max.apply(this,$.map($.parseJSON(str), function(o){ return o.y; }));
Lachman answered 26/10, 2010 at 5:34 Comment(1)
It's not tagged with jQueryKaitlinkaitlyn
E
17

Here is the shortest solution (One Liner) ES6:

Math.max(...values.map(o => o.y));
Elburr answered 9/5, 2020 at 8:33 Comment(1)
again, this is unsafe for large arrays as it will cause a stack overflow crashTeary
C
15

if you (or, someone here) are free to use lodash utility library, it has a maxBy function which would be very handy in your case.

hence you can use as such:

_.maxBy(jsonSlice, 'y');
Chloroprene answered 19/10, 2016 at 7:17 Comment(0)
S
10

Or a simple sort! Keeping it real :)

array.sort((a,b)=>a.y<b.y)[0].y
Sunup answered 10/1, 2018 at 10:45 Comment(5)
Nice idea +1 (shortest code), but there is small bug - change a.y<a.y to b.y-a.y. Time complexity comparison here: https://mcmap.net/q/53908/-finding-the-max-value-of-a-property-in-an-array-of-objectsHenrietta
Finding the max is O(n). This is O(nlogn). Writing simple code is good as long as efficiency is not sacrificed.Foramen
@Foramen - actually Micro-optimisation is worth it when you have evidence that you're optimising a bottleneck.. In most cases, the simple code is better choice than high-efficient code.Henrietta
@KamilKiełczewski Both array comparisons in that article have the same time complexity the difference is in their coefficient. For instance, one takes n time units to find the solution while the other one is 7n. In time complexity theory, both of these are O(n). What we are talking about in the problem of finding max is the comparison of O(n) with O(n logn). Now if you can guarantee n doesn't exceed 10 then you can use your solution otherwise O(n) algorithm is always the winner and performance(user experience) is always prior to developer experience(ask industry people, they tell you that!).Foramen
@Foramen nope - even if your array have n=10000 elements user will not see difference - proof HERE. Performance optimisation is good only for app bottleneck (eg. you need to process large arrays) - but in most case a performance-focus is wrong approach and time(=money) wasting. This is well known code approach mistake - read more: "micro-optimisation"Henrietta
P
9

Using one function find the max obj.value return obj

 let List= [{votes:4},{votes:8},{votes:7}]


let objMax = List.reduce((max, curren) => max.votes > curren.votes ? max : curren);

console.log(objMax)

Or using two functions return max value

  Math.max(...List.map(el => el.votes)))
Pot answered 7/7, 2022 at 17:21 Comment(1)
This answer is far from what is being questioning here. This will return the object, not the value.Mok
S
8

Each array and get max value with Math.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);
Syllabic answered 12/7, 2018 at 13:22 Comment(2)
+1 but use y: data.reduce((max, point) => Math.max(max, point.y), data[0].y); Many of the other answers create unnecessary temporary arrays or do expensive sorting. Using reduce() plus Math.max() is memory and CPU efficient, and it's more readable.Gainey
As mentioned above, can replace initial value with -Infinity.Erastus
W
3

It returns the object simplified @andy polhill answare

var data = [{
    y: 90
  },
  {
    y: 9
  },
  {
    y: 8
  }
]


const max = data.reduce((prev, current) => ((prev.y > current.y) ?
  prev : current), 0) //returns object
console.log(max)
Wesleyanism answered 23/2, 2022 at 9:55 Comment(0)
H
2
var max = 0;                
jQuery.map(arr, function (obj) {
  if (obj.attr > max)
    max = obj.attr;
});
Hallux answered 26/8, 2014 at 12:39 Comment(0)
R
2

ES6 solution

Math.max(...array.map(function(o){return o.y;}))

For more details see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max

Recluse answered 28/9, 2017 at 21:19 Comment(0)
M
1
// Here is very simple way to go:

// Your DataSet.

let numberArray = [
  {
    "x": "8/11/2009",
    "y": 0.026572007
  },
  {
    "x": "8/12/2009",
    "y": 0.025057454
  },
  {
    "x": "8/13/2009",
    "y": 0.024530916
  },
  {
    "x": "8/14/2009",
    "y": 0.031004457
  }
]

// 1. First create Array, containing all the value of Y
let result = numberArray.map((y) => y)
console.log(result) // >> [0.026572007,0.025057454,0.024530916,0.031004457]

// 2.
let maxValue = Math.max.apply(null, result)
console.log(maxValue) // >> 0.031004457
Meimeibers answered 10/5, 2019 at 9:14 Comment(0)
C
1

Quick and dirty:

Object.defineProperty(Array.prototype, 'min',
{
    value: function(f)
    {
        f = f || (v => v);
        return this.reduce((a, b) => (f(a) < f(b)) ? a : b);
    }
});

Object.defineProperty(Array.prototype, 'max',
{
    value: function(f)
    {
        f = f || (v => v);
        return this.reduce((a, b) => (f(a) > f(b)) ? a : b);
    }
});

console.log([1,2,3].max());
console.log([1,2,3].max(x => x*(4-x)));
console.log([1,2,3].min());
console.log([1,2,3].min(x => x*(4-x)));
Candis answered 7/4, 2021 at 0:46 Comment(0)
C
1

Explanation for accepted answer and a more generalized way

If someone's here to find the max value among all such keys (a generalized way):

const temp1 = [
    {
        "name": "Month 8 . Week 1",
        "CATEGORY, Id 0": null,
        "CATEGORY, Id 1": 30.666666666666668,
        "CATEGORY, Id 2": 17.333333333333332,
        "CATEGORY, Id 3": 12.333333333333334,
        "TASK, Id 1": 30.666666666666668,
        "TASK, Id 2": 12.333333333333334,
        "TASK, Id 3": null,
        "TASK, Id 4": 5,
        "TASK, Id 5": null,
        "TASK, Id 6": null,
        "TASK, Id 7": null,
        "TASK, Id 8": null,
        "TASK, Id 9": null,
        "TASK, Id 10": null,
        "TASK, Id 12": null,
        "TASK, Id 14": null,
        "TASK, Id 16": null,
        "TASK, Id 17": null,
        "TASK, Id 26": 12.333333333333334
    },
    {
        "name": "Month 8 . Week 2",
        "CATEGORY, Id 0": 38,
        "CATEGORY, Id 1": null,
        "CATEGORY, Id 2": 12,
        "CATEGORY, Id 3": null,
        "TASK, Id 1": null,
        "TASK, Id 2": 15,
        "TASK, Id 3": null,
        "TASK, Id 4": null,
        "TASK, Id 5": null,
        "TASK, Id 6": 5,
        "TASK, Id 7": 5,
        "TASK, Id 8": 5,
        "TASK, Id 9": 5,
        "TASK, Id 10": null,
        "TASK, Id 12": null,
        "TASK, Id 14": null,
        "TASK, Id 16": null,
        "TASK, Id 17": null,
        "TASK, Id 26": 15
    },
    {
        "name": "Month 8 . Week 3",
        "CATEGORY, Id 0": 7,
        "CATEGORY, Id 1": 12.333333333333334,
        "CATEGORY, Id 2": null,
        "CATEGORY, Id 3": null,
        "TASK, Id 1": null,
        "TASK, Id 2": null,
        "TASK, Id 3": 12.333333333333334,
        "TASK, Id 4": null,
        "TASK, Id 5": null,
        "TASK, Id 6": null,
        "TASK, Id 7": null,
        "TASK, Id 8": null,
        "TASK, Id 9": null,
        "TASK, Id 10": null,
        "TASK, Id 12": null,
        "TASK, Id 14": 7,
        "TASK, Id 16": null,
        "TASK, Id 17": null,
        "TASK, Id 26": null
    },
    {
        "name": "Month 8 . Week 4",
        "CATEGORY, Id 0": null,
        "CATEGORY, Id 1": null,
        "CATEGORY, Id 2": 10,
        "CATEGORY, Id 3": 5,
        "TASK, Id 1": null,
        "TASK, Id 2": null,
        "TASK, Id 3": null,
        "TASK, Id 4": null,
        "TASK, Id 5": 5,
        "TASK, Id 6": null,
        "TASK, Id 7": null,
        "TASK, Id 8": null,
        "TASK, Id 9": null,
        "TASK, Id 10": 5,
        "TASK, Id 12": 5,
        "TASK, Id 14": null,
        "TASK, Id 16": null,
        "TASK, Id 17": null,
        "TASK, Id 26": null
    },
    {
        "name": "Month 8 . Week 5",
        "CATEGORY, Id 0": 5,
        "CATEGORY, Id 1": null,
        "CATEGORY, Id 2": 7,
        "CATEGORY, Id 3": null,
        "TASK, Id 1": null,
        "TASK, Id 2": null,
        "TASK, Id 3": null,
        "TASK, Id 4": null,
        "TASK, Id 5": null,
        "TASK, Id 6": null,
        "TASK, Id 7": null,
        "TASK, Id 8": null,
        "TASK, Id 9": null,
        "TASK, Id 10": null,
        "TASK, Id 12": null,
        "TASK, Id 14": null,
        "TASK, Id 16": 7,
        "TASK, Id 17": 5,
        "TASK, Id 26": null
    },
    {
        "name": "Month 9 . Week 1",
        "CATEGORY, Id 0": 13.333333333333334,
        "CATEGORY, Id 1": 13.333333333333334,
        "CATEGORY, Id 3": null,
        "TASK, Id 11": null,
        "TASK, Id 14": 6.333333333333333,
        "TASK, Id 17": null,
        "TASK, Id 18": 7,
        "TASK, Id 19": null,
        "TASK, Id 20": null,
        "TASK, Id 26": 13.333333333333334
    },
    {
        "name": "Month 9 . Week 2",
        "CATEGORY, Id 0": null,
        "CATEGORY, Id 1": null,
        "CATEGORY, Id 3": 13.333333333333334,
        "TASK, Id 11": 5,
        "TASK, Id 14": null,
        "TASK, Id 17": 8.333333333333334,
        "TASK, Id 18": null,
        "TASK, Id 19": null,
        "TASK, Id 20": null,
        "TASK, Id 26": null
    },
    {
        "name": "Month 9 . Week 3",
        "CATEGORY, Id 0": null,
        "CATEGORY, Id 1": 14,
        "CATEGORY, Id 3": null,
        "TASK, Id 11": null,
        "TASK, Id 14": null,
        "TASK, Id 17": null,
        "TASK, Id 18": null,
        "TASK, Id 19": 7,
        "TASK, Id 20": 7,
        "TASK, Id 26": null
    }
]

console.log(Math.max(...[].concat([], ...temp1.map(i => Object.values(i))).filter(v => typeof v === 'number')))

one thing to note that Math.max(1, 2, 3) returns 3. So does Math.max(...[1, 2, 3]), since Spread syntax can be used when all elements from an object or array need to be included in a list of some kind.

We will take advantage of this!

Let's assume an array which looks like:

var a = [{a: 1, b: 2}, {foo: 12, bar: 141}]

And the goal is to find the max (among any attribute), (here it is bar(141))

so to use Math.max() we need values in one array (so we can do ...arr)

First let's just separate all the numbers We can each that each item of the array a is an object. While iterating through each of them, Object.values(item) will give us all the values of that item in an array form, and we can use map to generate a new array with only values

So,

var p = a.map(item => Object.values(item)) // [ [1, 2], [12, 141] ]

Also,using concat,

[].concat([], ...arr), or just [].concat(...arr) on arr, [ [1, 2], [12, 141] ] flattens it to [1, 2, 12, 141]

So,

var f = [].concat(...p) // [1, 2, 12, 141]

since we have now an array of just numbers we do Math.max(...f):

var m = Math.max(...f) // 141
Chittagong answered 14/10, 2021 at 11:31 Comment(0)
S
1

This typescript function can be called to search for the largest value that can exist in a field of the objects of an array:

function getHighestField(objArray: any[], fieldName: string) {
  return Number(
    Math.max.apply(
      Math,
      objArray?.map(o => o[fieldName] || 0),
    ) || 0,
  );
}

With this values as an example:

const scoreBoard = [
  { name: 'player1', score: 4 },
  { name: 'player2', score: 9 },
  { name: 'player3', score: 7 }
]

You can call the function something this way:

const myHighestVariable = `This is the highest: ${getHighestField(scoreBoard, "score")}`;

The result will be something like this:

console.log(myHighestVariable);

This is the highest: 9

Sepoy answered 6/1, 2022 at 23:28 Comment(0)
B
0

It's very simple

     const array1 = [
  {id: 1, val: 60},
  {id: 2, val: 2},
  {id: 3, val: 89},
  {id: 4, val: 78}
];
const array2 = [1,6,8,79,45,21,65,85,32,654];
const max = array1.reduce((acc, item) => acc = acc > item.val ? acc : item.val, 0);
const max2 = array2.reduce((acc, item) => acc = acc > item ? acc : item, 0);

console.log(max);
console.log(max2);
Buatti answered 4/1, 2021 at 19:2 Comment(0)
V
0
const getMaxFromListByField = (list, field) => { 
    return list[list.map(it => it[field]).indexOf(Math.max(...list.map(it => it[field])))] 
}
Vries answered 8/9, 2021 at 14:2 Comment(1)
Please provide additional details in your answer. As it's currently written, it's hard to understand your solution.Tiein
C
0

carefull on null and empty and property not in array and empty array

if ((value && value.length > 0)) {
  var maxObj = (value && value.length > 0) value.reduce(function (prev, current) {
    return ((parseInt(prev["y"]) || 0) > (parseInt(current["y"]) || 0)) ? prev : current
  })
}
{
  // else logic here
}
Circumstantial answered 1/12, 2022 at 18:48 Comment(0)
H
0

Just a grain of salt on Andy's answer: One can use a filter before reducing to avoid passing empty arrays, which could mess everything.

const max = data
.filter(item=>item.id != undefined) // --> here
.reduce(function(prev, current) {return (prev.y > current.y) ? prev : current
    }) //returns object
Housel answered 12/6, 2023 at 21:6 Comment(0)
H
0

If you want to get only the max "y",

const maxY = data.reduce((y, current) => (y == undefined || current.y > y) ? current.y : y, undefined)

.

Hesketh answered 22/2 at 11:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.