Trovare il valore massimo di un attributo in una matrice di oggetti


413

Sto cercando un modo davvero rapido, pulito ed efficiente per ottenere il massimo valore "y" nella seguente sezione 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
  }
]

Un for-loop è l'unico modo per farlo? Sono appassionato di usare in qualche modo Math.max.


4
Come restituiresti l'oggetto e non solo il valore attr minimo trovato?
Mike Lyons,

1
A mio vantaggio, ho eseguito alcuni rapidi test di perf su questo. jsperf.com/finding-the-max-value-an-array-of-objects
Andy Polhill

Risposte:


741

Per trovare il yvalore massimo degli oggetti in array:

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

47
Potresti espandere questa risposta per mostrare come restituire l'oggetto in cui è stato trovato il valore massimo? Sarebbe molto utile, grazie!
Mike Lyons,

19
Ecco il violino! spero che questo possa aiutare qualcuno jsfiddle.net/45c5r246
mili

24
@MikeLyons se ti interessa ancora ottenere l'oggetto reale: jsfiddle.net/45c5r246/34
tobyodavies

11
Per favore, espandi la tua risposta!
John William Domingo,

12
FWIW la mia comprensione è quando si chiama si applica su una funzione che esegue la funzione con un valore specificato per thise una serie di argomenti specificati come un array. Il trucco è che applicare trasforma l'array in una serie di argomenti di funzioni reali. Quindi, in questo caso, alla fine chiama Math.max(0.0265, 0.0250, 0.024, 0.031)con thisla funzione dell'essere eseguita Math. Non riesco a capire perché dovrebbe essere Mathsinceramente, non penso che la funzione richieda un valido this. Oh, ecco una spiegazione corretta: stackoverflow.com/questions/21255138/…
Daniel C

263

Trova l'oggetto la cui proprietà "Y" ha il massimo valore in una matrice di oggetti

Un modo sarebbe quello di utilizzare Array ridurre ..

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 e versioni successive)

Se non è necessario supportare IE (solo Edge) o è possibile utilizzare un pre-compilatore come Babel, è possibile utilizzare la sintassi più concisa.

const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current)

7
Questa è una buona risposta, tuttavia, si desidera passare un valore iniziale o si ottiene un errore nel caso in cui l'array di dati sia vuoto. vale a dire per un indice di incremento automatico degli oggetti. const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current, 1)
juliangonzalez,

2
Sollevi un buon punto, probabilmente sceglierei nulloltre 1.
Andy Polhill

25
Si noti che questo restituisce l'oggetto che aveva il valore massimo non il valore massimo dall'oggetto. Questo può o meno essere quello che vuoi. Nel mio caso era quello che volevo. +1
Giovanni

1
Buona risposta complementare!
Legends,

Risposta eccellente! All'inizio, ho esitato a causa della riduzione, ma dovremo comunque iterare, quindi perché no?
Shapiro Yaacov,

147

ES6 pulito e semplice (Babele)

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

Il secondo parametro dovrebbe garantire un valore predefinito se arrayToSearchInè vuoto.


8
inoltre è bene sapere che restituisce -Infinity(un valore veritiero ) per un array vuoto
icl7126

1
Questo è supportato nella maggior parte dei browser moderni senza Babel ora.
Eugene Kulabuhov,

20
mentre ritorna -Infinityper un array vuoto, puoi passare un valore iniziale Math.max(...state.allProjects.map(o => o.id), 1);
juliangonzalez,

5
Questa dovrebbe essere la risposta accettata ora ... sicuramente l'approccio più conciso.
Nick

1
per gestire il caso per i valori negativi cambiare 0in arrayToSearchIn[0].y. Tempo complessità confronto: stackoverflow.com/a/53654364/860099
Kamil Kielczewski

43

Confronto di ONELINERS dell'albero che gestiscono il caso meno numeri (input in aarray):

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)

esempio modificabile qui . Idee da: maxA , maxB e maxC (l'effetto collaterale di maxB è che l'array aè cambiato perché sortè sul posto).

Per array più grandi Math.max...verrà generata un'eccezione: dimensione massima dello stack di chiamate superata (Chrome 76.0.3809, Safari 12.1.2, data 2019-09-13)


2
Metodi molto intelligenti per svolgere il compito. Nizza
TetraDev,

Siamo spiacenti, sottoposto a downgrade per errore e non è stato possibile annullare senza modificare la domanda perché è trascorso troppo tempo.
Günter Zöchbauer,

Grazie grande analisi.
d337,

grazie per questa suddivisione delle opzioni disponibili e delle differenze tra gli approcci.
FistOfFury

una cosa da sottolineare è che l'opzione B rende molto più semplice ottenere l'intero oggetto con il yvalore massimo , lasciando .yfine alla fine.
FistOfFury

24

Vorrei spiegare passo dopo passo la risposta accettata concisa :

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

Ulteriori informazioni:


Grande spiegazione! Potrebbe essere un po 'più facile da leggere se non fosse nei commenti in codice, ma comunque - ottimo lavoro
Martin

23

Bene, prima dovresti analizzare la stringa JSON, in modo da poter accedere facilmente ai suoi membri:

var arr = $.parseJSON(str);

Utilizzare il mapmetodo per estrarre i valori:

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

Quindi è possibile utilizzare l'array nel maxmetodo:

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

O come one-liner:

var highest = Math.max.apply(this,$.map($.parseJSON(str), function(o){ return o.y; }));

15
Non è taggato conjQuery
Robin van Baalen il

1
@RobinvanBaalen: Sì, hai ragione. È tuttavia taggato con JSON ma la risposta accettata lo ignora e tobyodavies lo ha rimosso anche dall'argomento della domanda ... Forse dovrei aggiungere jquery alla domanda ...;)
Guffa,

8
Non importa molto se @tobyodavies ha ignorato il fatto che è stato taggato json- non sta usando una libreria javascript esterna nella sua risposta :)
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}

2
In cosa differisce dalla risposta di @ AndyPolhill?
Lewis,

7

se tu (o qualcuno qui) sei libero di usare la lodashlibreria di utility, ha una funzione maxBy che sarebbe molto utile nel tuo caso.

quindi puoi usare come tale:

_.maxBy(jsonSlice, 'y');

6

O un tipo semplice! Mantenerlo reale :)

array.sort((a,b)=>a.y<b.y)[0].y

Bella idea +1 (codice più breve), ma c'è un piccolo bug - cambia a.y<a.yin b.y-a.y. Confronto complessità temporale qui: stackoverflow.com/a/53654364/860099
Kamil Kielczewski

2
Trovare il massimo è O (n). Questo è O (nlogn). Scrivere un codice semplice è buono purché l'efficienza non venga sacrificata.
Wildhammer,

@Wildhammer: in realtà la micro-ottimizzazione vale la pena quando hai prove che stai ottimizzando un collo di bottiglia. . Nella maggior parte dei casi, il codice semplice è una scelta migliore rispetto al codice ad alta efficienza.
Kamil Kiełczewski,

@ KamilKiełczewski Entrambi i confronti di array in quell'articolo hanno la stessa complessità temporale la differenza è nel loro coefficiente. Ad esempio, uno impiega n unità di tempo per trovare la soluzione mentre l'altro è 7n. Nella teoria della complessità temporale, entrambi sono O (n). Ciò di cui stiamo parlando nel problema di trovare max è il confronto di O (n) con O (n logn). Ora se puoi garantire che n non superi 10, allora puoi usare la tua soluzione altrimenti l'algoritmo O (n) è sempre il vincitore e le prestazioni (esperienza utente) sono sempre precedenti all'esperienza degli sviluppatori (chiedi alle persone del settore, te lo dicono!) .
Wildhammer

@Wildhammer no - anche se il tuo array ha n = 10000 elementi l'utente non vedrà QUI a prova di differenza . L'ottimizzazione delle prestazioni è valida solo per il collo di bottiglia delle app (ad esempio, è necessario elaborare array di grandi dimensioni), ma nella maggior parte dei casi un focus sulle prestazioni è un approccio errato e uno spreco di tempo (= denaro). Questo è un errore di approccio al codice noto - leggi di più: "micro-ottimizzazione"
Kamil Kiełczewski

3

Ogni array e ottiene il massimo valore con la matematica.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);

2

Ecco la soluzione più breve (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;
});


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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.