Encontrar el valor máximo de un atributo en una matriz de objetos

413

Estoy buscando una forma realmente rápida, limpia y eficiente de obtener el valor máximo "y" en el siguiente segmento 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
  }
]

¿Es un bucle for la única forma de hacerlo? Estoy interesado en usar de alguna manera Math.max.

Rio
fuente
44
¿Cómo devolvería el objeto y no solo el valor de atributo min encontrado?
Mike Lyons
1
Para mi propio beneficio, realicé algunas pruebas rápidas de rendimiento en esto. jsperf.com/finding-the-max-value-an-array-of-objects
Andy Polhill
1
JSBin de las soluciones jsbin.com/pagamujuge/edit?html,js,console
Andy Polhill

Respuestas:

741

Para encontrar el yvalor máximo de los objetos en array:

Math.max.apply(Math, array.map(function(o) { return o.y; }))
tobyodavies
fuente
48
¿Podría ampliar esta respuesta para mostrar cómo devolver el objeto en el que se encontró el valor máximo? Eso sería muy útil, gracias!
Mike Lyons
19
¡Aquí está el violín! Espero que esto ayude a alguien jsfiddle.net/45c5r246
mili
24
@MikeLyons si todavía te importa obtener el objeto real: jsfiddle.net/45c5r246/34
tobyodavies
11
¡Por favor expanda su respuesta!
John William Domingo
12
FWIW, entiendo que cuando llamas a apply en una función, ejecuta la función con un valor especificado thisy una serie de argumentos especificados como una matriz. El truco es que aplicar convierte la matriz en una serie de argumentos de funciones reales. Entonces, en este caso, finalmente llama Math.max(0.0265, 0.0250, 0.024, 0.031)con thisla función ejecutada Math. No puedo ver por qué debería ser Mathfrancamente, no creo que la función requiera una válida this. Ah, y aquí hay una explicación adecuada: stackoverflow.com/questions/21255138/…
Daniel C
263

Encuentre el objeto cuya propiedad "Y" tiene el mayor valor en una matriz de objetos

Una forma sería usar 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 y superior)

Si no necesita admitir IE (solo Edge), o puede usar un precompilador como Babel, puede usar la sintaxis más concisa.

const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current)
Andy Polhill
fuente
77
Esta es una buena respuesta, sin embargo, le gustaría pasar un valor inicial o recibiría un error en caso de que la matriz de datos esté vacía. es decir, para un índice de aumento automático de objetos. const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current, 1)
juliangonzalez
2
Usted plantea un buen punto, probablemente elegiría nullmás de 1.
Andy Polhill
25
Tenga en cuenta que esto devuelve el objeto que tenía el valor máximo, no el valor máximo del objeto. Esto puede o no ser lo que quieres. En mi caso, era lo que quería. +1
John
1
Buena respuesta complementaria!
Leyendas
Excelente respuesta! Al principio, dudé debido a la reducción, pero tendremos que repetir de todos modos, así que ¿por qué no?
shapiro yaacov
147

limpio y simple ES6 (Babel)

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

El segundo parámetro debe garantizar un valor predeterminado si arrayToSearchInestá vacío.

Vitaliy Kotov
fuente
8
también es bueno saber que devuelve -Infinity(un valor verdadero ) para una matriz vacía
icl7126
1
Esto es compatible con la mayoría de los navegadores modernos sin Babel ahora.
Eugene Kulabuhov
20
a medida que regresa -Infinitypara una matriz vacía, puede pasar un valor inicial Math.max(...state.allProjects.map(o => o.id), 1);
juliangonzalez
55
Esta debería ser la respuesta aceptada ahora ... definitivamente el enfoque más conciso.
nickb
1
para manejar mayúsculas y minúsculas cambie 0a arrayToSearchIn[0].y. Comparación de la complejidad del tiempo: stackoverflow.com/a/53654364/860099
Kamil Kiełczewski
43

Comparación de Tree ONELINERS que manejan mayúsculas y minúsculas (entrada en amatriz):

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)

ejemplo editable aquí . Ideas de: maxA , maxB y maxC (el efecto secundario de maxB es que la matriz ase cambia porque sortestá en su lugar).

Para matrices más grandes, Math.max...se lanzará una excepción: se excedió el tamaño máximo de la pila de llamadas (Chrome 76.0.3809, Safari 12.1.2, fecha 2019-09-13)

Kamil Kiełczewski
fuente
2
Métodos muy inteligentes para realizar la tarea. Niza
TetraDev
Lo sentimos, voté por error y no pude deshacerlo sin editar su pregunta porque pasó demasiado tiempo.
Günter Zöchbauer
Gracias gran análisis.
d337
Gracias por este desglose de las opciones disponibles y las diferencias entre los enfoques.
FistOfFury
Una cosa a destacar es que la opción B hace que sea mucho más fácil obtener todo el objeto con el yvalor máximo al dejarlo .yal final.
FistOfFury
24

Me gustaría explicar la breve respuesta aceptada paso a paso:

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

Más información:

congusbongus
fuente
¡Gran explicación! Podría ser un poco más fácil de leer si no estuviera en los comentarios de código, pero aún así, gran trabajo
Martin
23

Bueno, primero debe analizar la cadena JSON, para que pueda acceder fácilmente a sus miembros:

var arr = $.parseJSON(str);

Use el mapmétodo para extraer los valores:

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

Luego puede usar la matriz en el maxmétodo:

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

O como una frase:

var highest = Math.max.apply(this,$.map($.parseJSON(str), function(o){ return o.y; }));
Guffa
fuente
15
No está etiquetado conjQuery
Robin van Baalen
1
@RobinvanBaalen: Sí, tienes razón. Sin embargo, está etiquetado con JSON, pero la respuesta aceptada lo ignora, y tobyodavies también lo eliminó del tema de la pregunta ... Tal vez debería agregar jquery a la pregunta ...;)
Guffa
8
No importa mucho si @tobyodavies ignoró el hecho de que estaba etiquetado json: no está usando una biblioteca javascript externa en su respuesta :)
Robin van Baalen
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}
Vin S
fuente
2
¿Cómo difiere esto de la respuesta de @ AndyPolhill?
Lewis
7

si usted (o alguien aquí) es libre de usar lodashla biblioteca de utilidades, tiene una función maxBy que sería muy útil en su caso.

por lo tanto, puede usar como tal:

_.maxBy(jsonSlice, 'y');
kmonsoor
fuente
6

O un tipo simple! Siendo realistas :)

array.sort((a,b)=>a.y<b.y)[0].y
Ooki Koi
fuente
Buena idea +1 (código más corto), pero hay un pequeño error: cambie a.y<a.ya b.y-a.y. Comparación de la complejidad del tiempo aquí: stackoverflow.com/a/53654364/860099
Kamil Kiełczewski
2
Encontrar el máximo es O (n). Esto es O (nlogn). Escribir código simple es bueno siempre que no se sacrifique la eficiencia.
Martillo salvaje
@Wildhammer: en realidad, la micro-optimización vale la pena cuando tienes evidencia de que estás optimizando un cuello de botella. . En la mayoría de los casos, el código simple es una mejor opción que el código de alta eficiencia.
Kamil Kiełczewski
@ KamilKiełczewski Ambas comparaciones de matrices en ese artículo tienen la misma complejidad de tiempo, la diferencia está en su coeficiente. Por ejemplo, uno toma n unidades de tiempo para encontrar la solución, mientras que el otro es 7n. En la teoría de la complejidad del tiempo, ambos son O (n). De lo que estamos hablando en el problema de encontrar max es la comparación de O (n) con O (n logn). Ahora, si puede garantizar que n no exceda de 10, puede usar su solución; de lo contrario, el algoritmo O (n) siempre es el ganador y el rendimiento (experiencia del usuario) siempre es anterior a la experiencia del desarrollador (¡pregunte a la gente de la industria, se lo dicen!) .
Martillo salvaje
@Wildhammer no: incluso si su matriz tiene n = 10000 elementos, el usuario no verá la diferencia a prueba AQUÍ . La optimización del rendimiento es buena solo para el cuello de botella de la aplicación (por ejemplo, necesita procesar grandes matrices), pero en la mayoría de los casos, un enfoque de rendimiento es un enfoque incorrecto y una pérdida de tiempo (= dinero). Este es un error de enfoque de código bien conocido - lea más: "micro-optimización"
Kamil Kiełczewski
3

Cada matriz y obtener el valor máximo con Math.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);
Diego Santa Cruz Mendezú
fuente
2

Aquí está la solución más corta (One Liner) ES6 :

Math.max(...values.map(o => o.y));
Subodh Singh
fuente
1
var max = 0;                
jQuery.map(arr, function (obj) {
  if (obj.attr > max)
    max = obj.attr;
});
Mephisto07
fuente
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
Pushp Singh
fuente