530

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.

np_6
  • 506
  • 6
  • 17
Rio
  • 11,734
  • 21
  • 62
  • 104

15 Answers15

926

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

Math.max.apply(Math, array.map(function(o) { return o.y; }))
Rory O'Kane
  • 25,436
  • 11
  • 86
  • 123
tobyodavies
  • 23,274
  • 5
  • 37
  • 56
  • 59
    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! – Mike Lyons Sep 12 '14 at 21:27
  • please elaborate the usage with a fiddle. It would be helpful. – Ankit Tanna Jan 15 '15 at 19:04
  • 22
    Here is the fiddle! hope this will help to someone https://jsfiddle.net/45c5r246/ – mili May 20 '15 at 11:27
  • 27
    @MikeLyons if you still care about getting the actual object: https://jsfiddle.net/45c5r246/34/ – tobyodavies Jul 03 '15 at 00:45
  • 18
    Please expain your answer! – John William Domingo Aug 12 '16 at 23:35
  • 17
    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: http://stackoverflow.com/questions/21255138/how-does-the-math-max-apply-work – Daniel C Aug 18 '16 at 14:12
  • 1
    @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. – tobyodavies Aug 19 '16 at 04:48
  • 2
    `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. – Standaa - Monica side Sep 08 '16 at 10:21
  • Careful to call this only when the array is not empty, otherwise the returned value will be `Infinity` – philk Dec 14 '16 at 00:55
  • How do you return the object with the highest value?, like `{"x":"8/11/2009","y":0.026572007}` – Patrioticcow Mar 05 '17 at 23:28
  • 3
    Math.max(...array.map(o => o.y)) <3 thx to atom code formatter – Fabien May 21 '17 at 18:42
  • 2
    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? – Wanny Miarelli Oct 29 '17 at 09:54
  • 2
    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) – Tom22 Nov 13 '17 at 02:12
  • 3
    a more concise snippet to get the max value: `Math.max(...array.map(o => o.y))` – Amnon Oct 16 '19 at 14:11
  • @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) )` – KevynTD May 17 '20 at 07:45
  • if we use this with react native the app might get crashed if there is large amount of data – Samiksha Jagtap Aug 12 '20 at 06:04
  • 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: https://stackoverflow.com/questions/30809656/how-to-avoid-stack-overflow-when-applying-max-to-a-large-array-in-javascript/30809791 – Sam Watkins Feb 02 '21 at 06:07
371

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.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.y > current.y) ? prev : current)
Andy Polhill
  • 5,078
  • 2
  • 22
  • 20
  • 10
    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)` – juliangonzalez May 25 '17 at 15:56
  • 3
    You raise a good point, I would probably choose `null` over 1. – Andy Polhill May 30 '17 at 10:26
  • 44
    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. +1 – John Nov 24 '17 at 23:46
  • 1
    Good complementary answer ! – Legends Jun 28 '18 at 20:33
  • Excellent answer! At first, I hesitated due to the reduce, but we're gonna need to iterate anyway, so why not? – shapiro yaacov Feb 26 '19 at 10:09
  • This is better than the accepted answer, because it's unsafe to call apply with a large array, it can cause stack overflow: https://stackoverflow.com/questions/30809656/how-to-avoid-stack-overflow-when-applying-max-to-a-large-array-in-javascript/30809791 – Sam Watkins Feb 02 '21 at 06:07
  • @AndyPolhill if you pass `null` instead of `1` as an initial value, then you get `TypeError: Cannot read property 'y' of null` – gaitat Mar 25 '21 at 17:04
212

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.

Alexei - check Codidact
  • 17,850
  • 12
  • 118
  • 126
Vitaliy Kotov
  • 2,942
  • 1
  • 12
  • 5
98

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 here. 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

Sam Watkins
  • 6,451
  • 3
  • 34
  • 35
Kamil Kiełczewski
  • 53,729
  • 20
  • 259
  • 241
  • 3
    Very clever methods to accomplish the task. Nice – TetraDev Aug 07 '19 at 20:43
  • thank you for this breakdown of the options available and the differences between approaches. – FistOfFury Apr 01 '20 at 15:39
  • 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. – FistOfFury Apr 01 '20 at 15:51
  • 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? – GreatHawkeye Apr 03 '20 at 09:36
  • @GreatHawkeye - yes you are right - fixed - thank you – Kamil Kiełczewski Apr 03 '20 at 10:45
  • For those curious about the difference (if any) between O(n) and O(2n) see https://stackoverflow.com/questions/25777714/which-algorithm-is-faster-on-or-o2n – jhovanec Sep 23 '20 at 20:49
  • @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) – Kamil Kiełczewski Sep 23 '20 at 20:58
  • 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://stackoverflow.com/a/25899325/2578125, best bet is O(n), though actual usage could vary. – OXiGEN Sep 26 '20 at 07:55
  • Nice use of reduce – MaxPower Apr 17 '21 at 08:52
27

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:

Community
  • 1
  • 1
congusbongus
  • 10,870
  • 5
  • 64
  • 86
  • Great explanation! It might be a bit easier to read if it wasn't in code comments, but still - great work – Martin Jan 18 '17 at 22:01
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; }));
Guffa
  • 640,220
  • 96
  • 678
  • 956
  • 20
    It's not tagged with `jQuery` – Robin van Baalen Aug 25 '14 at 16:09
  • 1
    @RobinvanBaalen: Yes, you are right. It is however tagged with JSON but the accepted answer ignores that, and tobyodavies also removed that from the subject of the question... Perhaps I should add jquery to the question... ;) – Guffa Aug 25 '14 at 17:54
  • 9
    It doesn't matter much if @tobyodavies ignored the fact that it was tagged `json` -- he isn't using an external javascript library in his answer :) – Robin van Baalen Aug 25 '14 at 18:33
12

Here is the shortest solution (One Liner) ES6:

Math.max(...values.map(o => o.y));
Abdelsalam Shahlol
  • 1,171
  • 1
  • 17
  • 26
Subodh Singh
  • 189
  • 2
  • 4
11

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');
VLAZ
  • 18,437
  • 8
  • 35
  • 54
kmonsoor
  • 5,913
  • 6
  • 38
  • 52
8

Or a simple sort! Keeping it real :)

array.sort((a,b)=>a.y<b.y)[0].y
Ooki Koi
  • 230
  • 4
  • 2
  • Nice idea +1 (shortest code), but there is small bug - change `a.y – Kamil Kiełczewski Dec 07 '18 at 11:16
  • 2
    Finding the max is O(n). This is O(nlogn). Writing simple code is good as long as efficiency is not sacrificed. – Wildhammer Feb 14 '20 at 16:52
  • @Wildhammer - actually [Micro-optimisation is worth it when you have evidence that you're optimising a bottleneck.](https://stackoverflow.com/a/3471000/860099). In most cases, the simple code is better choice than high-efficient code. – Kamil Kiełczewski Apr 01 '20 at 16:47
  • @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!). – Wildhammer Apr 01 '20 at 19:32
  • @Wildhammer nope - even if your array have n=10000 elements user will not see difference - proof [HERE](https://jsfiddle.net/Lamik/xg1th6c5/). 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" – Kamil Kiełczewski Apr 01 '20 at 20:25
5

Each array and get max value with Math.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);
  • +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. – Dem Pilafian Sep 12 '20 at 07:13
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

ndey96
  • 79
  • 6
1
var max = 0;                
jQuery.map(arr, function (obj) {
  if (obj.attr > max)
    max = obj.attr;
});
Mephisto07
  • 29
  • 1
0
// 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
Ryan
  • 25
  • 4
Pushp Singh
  • 537
  • 7
  • 10
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);
Rock Dial
  • 49
  • 2
0

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)));
trinalbadger587
  • 1,097
  • 1
  • 12
  • 25