Нахождение максимального значения атрибута в массиве объектов

413

Я ищу действительно быстрый, чистый и эффективный способ получить максимальное значение "y" в следующем фрагменте JSON:

[
  {
    "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
  }
]

Является ли цикл for единственный способ сделать это? Я заинтересован в том, чтобы как-то использовать Math.max.

Rio
источник
4
Как бы вы вернули объект, а не только найденное минимальное значение атрибута?
Майк Лайонс,
1
Для собственной выгоды я провел несколько быстрых тестов на это. jsperf.com/finding-the-max-value-an-array-of-objects
Энди Полхилл,
1
JSBin из решений jsbin.com/pagamujuge/edit?html,js,console
Энди Полхилл

Ответы:

742

Чтобы найти максимальное yзначение объектов в array:

Math.max.apply(Math, array.map(function(o) { return o.y; }))
tobyodavies
источник
48
Не могли бы вы расширить этот ответ, чтобы показать, как вернуть объект, в котором было найдено максимальное значение? Это было бы очень полезно, спасибо!
Майк Лайонс,
19
Вот скрипка! надеюсь , это поможет кому - то jsfiddle.net/45c5r246
Mili
24
@MikeLyons, если вы все еще хотите получить реальный объект: jsfiddle.net/45c5r246/34
tobyodavies
11
Пожалуйста, объясните свой ответ!
Джон Уильям Доминго
12
Я понимаю, что когда вы вызываете apply для функции, она выполняет функцию с заданным значением thisи рядом аргументов, указанным в виде массива. Хитрость в том, что apply превращает массив в серию реальных аргументов функции. Так что в этом случае он , наконец , вызывает Math.max(0.0265, 0.0250, 0.024, 0.031)с thisфункцией , выполняемой быть Math. Я не могу понять, почему это должно быть Mathчестно, я не думаю, что функция требует действительной this. Да, и вот правильное объяснение: stackoverflow.com/questions/21255138/…
Даниэль C
263

Найти объект, свойство которого «Y» имеет наибольшее значение в массиве объектов

Один из способов - использовать 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 и выше)

Если вам не нужна поддержка IE (только Edge) или вы можете использовать прекомпилятор, такой как Babel, вы можете использовать более краткий синтаксис.

const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current)
Энди Полхилл
источник
7
Это хороший ответ, однако вы хотели бы передать начальное значение, иначе вы получите ошибку, если массив данных пуст. т.е. для автоинкрементного индекса объектов. const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current, 1)
Juliangonzalez
2
Вы подняли хороший вопрос, я бы, вероятно, выбрал nullболее 1.
Энди Полхилл
25
Обратите внимание, что это возвращает объект, который имел максимальное значение, а не максимальное значение из объекта. Это может или не может быть то, что вы хотите. В моем случае это было то, что я хотел. +1
Джон
1
Хороший дополнительный ответ!
Легенды
Отличный ответ! Сначала я колебался из-за сокращения, но нам все равно придется повторяться, так почему бы и нет?
Шапиро Яаков
147

чистый и простой ES6 (Бабель)

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

Второй параметр должен обеспечивать значение по умолчанию, если arrayToSearchInоно пустое.

Виталий Котов
источник
8
также хорошо знать, что он возвращает -Infinity( истинное значение) для пустого массива
icl7126
1
Это поддерживается в большинстве современных браузеров без Babel.
Евгений Кулабухов
20
так как он возвращает -Infinityпустой массив, вы можете передать начальное значение Math.max(...state.allProjects.map(o => o.id), 1);
juliangonzalez
5
Это должен быть принятый ответ сейчас ... определенно более краткий подход.
Ника
1
обрабатывать регистр для минус значения меняются 0на arrayToSearchIn[0].y. Сравнение сложности времени: stackoverflow.com/a/53654364/860099
Камиль Кельчевский
43

Сравнение дерева ONELINERS, которые обрабатывают регистр минус числа (вход в aмассив):

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)

редактируемый пример здесь . Идеи из: maxA , maxB и maxC (побочный эффект maxB заключается в том, что массив aизменяется, потому что sortон на месте).

Для больших массивов возникает Math.max...исключение: превышен максимальный размер стека вызовов (Chrome 76.0.3809, Safari 12.1.2, дата 2019-09-13)

Камил Келчевски
источник
2
Очень умные методы для выполнения задачи. Ницца
TetraDev
Извините, проголосовал по ошибке и не смог отменить без редактирования вашего вопроса, потому что прошло слишком много времени.
Гюнтер Цохбауэр
Спасибо отличный анализ.
d337
Спасибо за эту разбивку доступных вариантов и различия между подходами.
FistOfFury
Стоит отметить, что опция B значительно упрощает получение всего объекта с максимальным yзначением, если оставить его .yв конце.
FistOfFury
24

Я хотел бы объяснить шаг за шагом принятый ответ :

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>');

Дальнейшая информация:

congusbongus
источник
Отличное объяснение! Возможно, было бы немного легче читать, если бы это не было в комментариях к коду, но все же - отличная работа
Martin
23

Ну, во-первых, вы должны проанализировать строку JSON, чтобы вы могли легко получить доступ к ее членам:

var arr = $.parseJSON(str);

Используйте mapметод для извлечения значений:

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

Тогда вы можете использовать массив в maxметоде:

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

Или как однострочник:

var highest = Math.max.apply(this,$.map($.parseJSON(str), function(o){ return o.y; }));
Guffa
источник
15
Это не с тегомjQuery
Робин ван Баален
1
@RobinvanBaalen: Да, вы правы. Однако он помечен JSON, но принятый ответ игнорирует это, и tobyodavies также удалили это из предмета вопроса ... Возможно, я должен добавить jquery к вопросу ...;)
Guffa
8
Не имеет большого значения, если @tobyodavies проигнорировал тот факт, что он был помечен json- он не использует внешнюю библиотеку javascript в своем ответе :)
Робин ван Баален
12
var data = [
  { 'name': 'Vins', 'age': 27 },
  { 'name': 'Jan', 'age': 38 },
  { 'name': 'Alex', 'age': 80 },
  { 'name': 'Carl', 'age': 25 },
  { 'name': 'Digi', 'age': 40 }
];
var max = data.reduce(function (prev, current) {
   return (prev.age > current.age) ? prev : current
});
//output = {'name': 'Alex', 'age': 80}
Вин С
источник
2
Чем это отличается от ответа @ AndyPolhill?
Льюис
7

если вы (или кто-то здесь) можете свободно использовать lodashслужебную библиотеку, она имеет функцию maxBy, которая была бы очень удобна в вашем случае.

следовательно, вы можете использовать как таковой:

_.maxBy(jsonSlice, 'y');
kmonsoor
источник
6

Или простой вид! Сохраняя это реальным :)

array.sort((a,b)=>a.y<b.y)[0].y
Ооки Кои
источник
Хорошая идея +1 (самый короткий код), но есть небольшая ошибка - измените a.y<a.yна b.y-a.y. Сравнение сложности времени здесь: stackoverflow.com/a/53654364/860099
Камиль Килчевски
2
Нахождение максимума O (n). Это O (nlogn). Написание простого кода хорошо до тех пор, пока эффективность не принесена в жертву.
Wildhammer
@Wildhammer - на самом деле микрооптимизация того стоит, когда у вас есть доказательства того, что вы оптимизируете узкое место. , В большинстве случаев простой код является лучшим выбором, чем высокоэффективный код.
Камиль Келчевски
@ KamilKiełczewski Оба сравнения массивов в этой статье имеют одинаковую сложность по времени, разница заключается в их коэффициенте. Например, одному требуется n единиц времени, чтобы найти решение, а другому - 7n. В теории временной сложности оба являются O (n). В проблеме нахождения max речь идет о сравнении O (n) с O (n logn). Теперь, если вы можете гарантировать, что n не превосходит 10, тогда вы можете использовать свое решение, в противном случае алгоритм O (n) всегда является победителем, а производительность (пользовательский опыт) всегда предшествует опыту разработчика (спросите специалистов отрасли, они скажут вам это!) ,
Wildhammer
@Wildhammer Нет - даже если в вашем массиве n = 10000 элементов, пользователь не увидит защищенных от различий ЗДЕСЬ . Оптимизация производительности хороша только для узких мест приложения (например, вам нужно обрабатывать большие массивы), но в большинстве случаев фокусирование на производительности - это неправильный подход и потеря времени (= денег). Это общеизвестная ошибка подхода к коду - подробнее: «
микрооптимизация
3

Каждый массив и получить максимальное значение с Math.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);
Диего Санта Круз Мендесу
источник
2

Вот самое короткое решение (One Liner) ES6 :

Math.max(...values.map(o => o.y));
Субодх Сингх
источник
1
var max = 0;                
jQuery.map(arr, function (obj) {
  if (obj.attr > max)
    max = obj.attr;
});
Mephisto07
источник
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
Пуш Сингх
источник