Articles

Come trovare l’id massimo in un array di oggetti in JavaScript

Posted on

Di recente stavo mettendo insieme un tutorial per alcuni studenti e mi sono imbattuto in un problema che sono sicuro abbiamo incontrato tutti prima o poi. Avevo questo array di oggetti e in ogni oggetto avevo un id. Volevo un modo veloce ed efficiente per guardare tutti gli id e trovare l’id massimo.

In questo articolo vi guiderò attraverso 4 diverse soluzioni.

  • Array.forEach
  • Array.map
  • Array.reduce
  • Math.max

Impostazione del progetto

Io sto usando Node per eseguire questo esempio, ma si può facilmente usare JavaScript e inserirlo in una pagina web. La prima cosa che farò è richiedere il modulo assert di Node che ci dà la possibilità di fornire un semplice set di test di asserzione.

const assert = require("assert");

In seguito creeremo un array di caratteri. Recentemente ho letto il libro Bad Blood, così ho deciso di usare alcuni dei personaggi di quel libro. Nota a margine: se non avete ancora letto quel libro, ve lo consiglio vivamente. Ogni personaggio nell’array sarà un oggetto che contiene un id, nome e cognome.

const characters = ;

In ogni possibile soluzione si vuole scrivere una logica che restituisca l’id più grande nell’array.

Array.forEach

Il vostro primo pensiero potrebbe essere quello di iterare sull’array usando qualche tipo di ciclo e perché no, è quello che vi hanno insegnato a fare da quando avete iniziato a scrivere codice. Potete iniziare dichiarando un massimo di zero, iterare su ogni carattere e se il suo id è più grande del massimo, aggiornare il massimo.

let max = 0;characters.forEach(character => { if (character.id > max) { max = character.id; }});assert(max === 444);

Il mio obiettivo principale ogni volta che scrivo codice è di far funzionare qualcosa prima e poi migliorarlo in seguito. Se si dovesse eseguire questo codice funziona certamente, ma non mi sembra giusto. E se avete mille o un milione di oggetti nell’array? Ogni volta che comincio ad iterare su un array usando un qualche tipo di ciclo per eseguire un calcolo è un’enorme bandiera rossa per me.

Array.map

Ogni volta che arriva la bandiera rossa dell’iterazione su un array mi chiedo immediatamente se questo è qualcosa che map/filter/reduce può risolvere. Quindi, se avete intenzione di adottare questo approccio, potete iniziare con il metodo map. Il metodo map() crea un nuovo array con i risultati della chiamata di una funzione fornita su ogni elemento dell’array chiamato. Userete il metodo map per creare un nuovo array che contiene solo gli id, così non lavorerete più con gli oggetti. A questo punto potete semplicemente usare il normale metodo di ordinamento sull’array, prendere l’ultimo elemento della lista e quello è il vostro id massimo.

const ids = characters.map(user => user.id);const sorted = ids.sort((a, b) => a - b);assert(sorted === 444);

Se volete essere davvero fantasiosi potete fare tutto questo in una singola dichiarazione.

assert( characters.map(user => user.id).sort((a, b) => a - b) === 444);

Array.reduce

Il metodo reduce() esegue una funzione reducer (che voi fornite) su ogni membro dell’array ottenendo un singolo valore in uscita. Qui chiamerete reduce sull’array di caratteri. Definirete un accumulatore che accumulerà il valore di ritorno della callback. Ogni volta che la funzione di callback viene chiamata restituirà un valore e lo memorizzerà in max. Il metodo di callback guarda l’id del carattere e se è maggiore di max lo restituisce, altrimenti restituisce max. Infine ha bisogno di impostare un valore di default per max e questo è ciò che characters.id sta facendo.

const maxId = characters.reduce( (max, character) => (character.id > max ? character.id : max), characters.id);assert(maxId === 444);

Se notate questo è un approccio simile a quello che usiamo con il metodo forEach, iterando su ogni elemento e confrontandolo con max. La differenza qui è che usare reduce è molto più veloce. Potete anche usare una combinazione di reduce e la soluzione successiva Math.max() se volete.

L’operatore Spread

Mentre l’approccio map e reduce mi piace molto di più dell’iterazione sull’array non mi sembra ancora il massimo. Se scavate in Math.max scoprirete che può prendere una lista di numeri o un array di numeri.

Questo significa che tutto quello che dovete fare è ottenere un array di id e potete passarlo nella funzione max(). Se siete stati attenti, lo avete già fatto prima usando map. Questo significa che potete usare la funzione map per ottenere un array di id e poi passarlo nella funzione max() usando l’operatore spread.

assert(Math.max(...characters.map(user => user.id)) === 444);

La sintassi spread permette ad un iterabile come un’espressione di array o una stringa di essere espansa in posti dove ci si aspettano zero o più argomenti (per le chiamate a funzioni) o elementi (per i letterali di array), o un’espressione oggetto di essere espansa in posti dove ci si aspettano zero o più coppie chiave-valore (per i letterali di oggetto).

Questa per me è la soluzione dall’aspetto più pulito e mi piace molto.

Risultati delle prestazioni

Non volevo trasformare questo in un articolo sulla programmazione funzionale in JavaScript ma è qui che siamo arrivati. Ho fatto un po’ di cronometraggio di base di ogni funzione (usando console.time() &console.timeEnd()) e ho trovato i seguenti risultati.

  • test di iterazione: 0.217ms
  • test di mappatura: 0.191ms
  • reduce test: 0.144ms
  • test di diffusione: 0.148ms

Conclusione

Mentre l’uso di reduce ci ha dato un leggero vantaggio in termini di prestazioni, preferisco usare Math.max e l’operatore spread. Per me sembra semplicemente più pulito ed è qualcosa che mi piace scrivere. Se vi fosse dato questo problema, quale soluzione adottereste? Me ne manca qualcuna? Spero che vi sia piaciuto camminare attraverso come risolverei questo problema e fino alla prossima volta…

Happy CodingDan

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *