[ Web Proxy ]
URL:
Viewing: https://it.javascript.info/array-methods [Back]  [Original]

Metodi per gli array
IT

Vorremo rendere disponibile questo progetto open-source per persone in tutto il mondo.

Aiutaci a tradurre il contenuto di questo tutorial nella tua lingua!

    Cerca in Javascript.info:
    Cerca nel tutorial:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Metodi per gli array

    Gli array forniscono una gran quantit di metodi. Per rendere le cose pi semplici, in questo capitolo li abbiamo divisi per gruppi.

    Aggiungere/rimuovere elementi

    Conosciamo gi i metodi che consentono di aggiungere e rimuovere elementi:

    • arr.push(...items) aggiunge un elemento in coda,
    • arr.pop() estrae un elemento dalla coda,
    • arr.shift() estrae un elemento dalla testa,
    • arr.unshift(...items) aggiunge un elemento in testa.

    Vediamone altri.

    splice

    Come cancellare un elemento dallarray?

    Gli array sono oggetti, quindi possiamo provare ad utilizzare delete:

    let arr = ["I", "go", "home"];
    
    delete arr[1]; // rimuove "go"
    
    alert( arr[1] ); // undefined
    
    // ora arr = ["I",  , "home"];
    alert( arr.length ); // 3

    Lelemento viene rimosso, ma larray ha ancora 3 elementi, possiamo vederlo tramite arr.length == 3.

    Non una sorpresa, perch delete obj.key rimuove un valore dalla key. Questo tutto quello che fa. Pu andare bene per gli oggetti. Con gli array vorremmo che il resto degli elementi scalassero, andando ad occupare il posto che si liberato. Per questo ci aspetteremmo di avere un array pi corto.

    Per questo scopo sono stati sviluppati dei metodi dedicati.

    Il metodo arr.splice un coltellino svizzero per array. Pu fare qualsiasi cosa: aggiungere e rimuovere elementi, ovunque.

    La sintassi :

    arr.splice(start[, deleteCount, elem1, ..., elemN])

    Modifica larray partendo dallindice start; rimuove deleteCount elementi ed inserisce elem1, ..., elemN. Infine ritorna un array contenente gli elementi rimossi.

    Questo metodo facile da capire tramite esempi.

    Proviamo ad eliminare degli elementi:

    let arr = ["I", "study", "JavaScript"];
    
    arr.splice(1, 1); // a partire da indice 1 rimuove 1 elemento
    
    alert( arr ); // ["I", "JavaScript"]

    Facile, vero? Ha rimosso 1 elemento, a partire dallelemento 1.

    Nel prossimo esempio, rimuoviamo 3 elementi e li rimpiazziamo con altri due:

    let arr = ["I", "study", "JavaScript", "right", "now"];
    
    // rimuove i primi 3 elementi e li rimpiazza con altri
    arr.splice(0, 3, "Let's", "dance");
    
    alert( arr ) // ora ["Let's", "dance", "right", "now"]

    Possiamo vedere larray ritornato da splice contenente gli elementi rimossi:

    let arr = ["I", "study", "JavaScript", "right", "now"];
    
    // rimuove i primi 2 elementi
    let removed = arr.splice(0, 2);
    
    alert( removed ); // "I", "study" <-- array di elementi rimossi

    Il metodo splice anche in grado di inserire elementi senza alcuna rimozione. Per ottenere questo dobbiamo impostare deleteCount a 0:

    let arr = ["I", "study", "JavaScript"];
    
    // da indice 2
    // ne rimuove 0
    // poi inserisce "complex" e "language"
    arr.splice(2, 0, "complex", "language");
    
    alert( arr ); // "I", "study", "complex", "language", "JavaScript"
    Sono permessi indici negativi

    In questo come in altri metodi dedicati agli array, sono permessi indici negativi. Essi specificano la posizione dalla fine dellarray, come:

    let arr = [1, 2, 5];
    
    // dall'indice -1 (un passo dalla fine)
    // cancella 0 elementi,
    // poi inserisce 3 e 4
    arr.splice(-1, 0, 3, 4);
    
    alert( arr ); // 1,2,3,4,5

    slice

    Il metodo arr.slice pi semplice di arr.splice.

    La sintassi :

    arr.slice([start], [end])

    Ritorna un nuovo array contente tutti gli elementi a partire da "start" fino ad "end" ("end" escluso). Sia start che end possono essere negativi; in tal caso si inizier a contare dalla coda dellarray.

    Funziona come str.slice, ma crea dei sotto-array piuttosto che sotto-stringhe.

    Ad esempio:

    let arr = ["t", "e", "s", "t"];
    
    alert( arr.slice(1, 3) ); // e,s (copia da 1 a 3)
    
    alert( arr.slice(-2) ); // s,t (copia da -2 fino alla fine)

    Possiamo anche utilizzarlo senza argomenti: arr.slice() crea una copia di arr. Questo tipo di chiamata spesso utilizzata per creare una copia con cui poter liberamente lavorare senza modificare larray originale.

    concat

    Il metodo arr.concat crea un nuovo array che include valori di altri array, o elementi aggiuntivi.

    La sintassi :

    arr.concat(arg1, arg2...)

    Accetta un numero arbitrario di argomenti sia array che valori.

    Il risultato un nuovo array contenente gli elementi di arr, seguiti da arg1, arg2 etc.

    Se un argomento argN un array, tutti i suoi elementi vengono copiati. Altrimenti viene copiato solamente largomento stesso.

    Un esempio:

    let arr = [1, 2];
    
    // unisce arr con [3,4]
    alert( arr.concat([3, 4])); // 1,2,3,4
    
    // unisce arr con [3,4] e [5,6]
    alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6
    
    // unisce arr con [3,4], poi aggiunge i valori 5 e 6
    alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6

    Normalmente copia elementi da un array. Gli altri oggetti, anche se assomigliano molto ad un array, vengono aggiunti interamente:

    let arr = [1, 2];
    
    let arrayLike = {
      0: "something",
      length: 1
    };
    
    alert( arr.concat(arrayLike) ); // 1,2,[object Object]

    Se, invece, un oggetto simile ad un array possiede la propriet Symbol.isConcatSpreadable, allora viene trattato come un array e i suoi elementi vengono copiati:

    let arr = [1, 2];
    
    let arrayLike = {
      0: "something",
      1: "else",
      [Symbol.isConcatSpreadable]: true,
      length: 2
    };
    
    alert( arr.concat(arrayLike) ); // 1,2,something,else

    Iterate: forEach

    Il metodo arr.forEach consente di eseguire una funzione su ogni elemento dellarray.

    La sintassi:

    arr.forEach(function(item, index, array) {
      // ... fa qualcosa con l'elemento
    });

    Ad esempio, il codice sotto mostra ogni elemento dellarray:

    // per ogni elemento chiama alert
    ["Bilbo", "Gandalf", "Nazgul"].forEach(alert);

    Invece questo codice ne mostra anche la posizione:

    ["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
      alert(`${item} is at index ${index} in ${array}`);
    });

    Il risultato di questa funzione (sempre che ci sia) viene scartato.

    Ricerca in un array

    Ora vedremo dei metodi per effettuare ricerche in un array.

    indexOf/lastIndexOf e include

    I metodi arr.indexOf, arr.lastIndexOf e arr.includes hanno la stessa sintassi, e fanno praticamente la stessa cosa della loro controparte per stringhe, ma operano su elementi invece che su caratteri:

    • arr.indexOf(item, from) cerca un item a partire dallindirizzo from, e ritorna lindirizzo in cui stato trovato, altrimenti ritorna -1.
    • arr.lastIndexOf(item, from) lo stesso, ma esegue la ricerca a partire da destra verso sinistra.
    • arr.includes(item, from) cerca un item a partire dallindice from, e ritorna true se lo trova.

    Ad esempio:

    let arr = [1, 0, false];
    
    alert( arr.indexOf(0) ); // 1
    alert( arr.indexOf(false) ); // 2
    alert( arr.indexOf(null) ); // -1
    
    alert( arr.includes(1) ); // true

    Da notare che questi metodi usano il confronto ===. Quindi, se cerchiamo false, troveremo esattamente false e non zero.

    Se vogliamo solo verificare la presenza di un elemento, senza voler conoscere lindirizzo, preferibile utilizzare il metodo arr.includes.

    Inoltre, una piccola differenza che includes gestisce correttamente NaN, a differenza di indexOf/lastIndexOf:

    const arr = [NaN];
    alert( arr.indexOf(NaN) ); // -1 (dovrebbe essere 0, ma l'uguaglianza === non funziona con NaN)
    alert( arr.includes(NaN) );// true (corretto)

    find e findIndex

    Immaginiamo di avere un array di oggetti. Come possiamo trovare un oggetto che soddisfi specifiche condizioni?

    In questi casi si utilizza il metodo arr.find.

    La sintassi :

    let result = arr.find(function(item, index, array) {
      // se viene ritornato true, viene ritornato l'elemento e l'iterazione si ferma
      // altrimenti ritorna undefined
    });

    La funzione viene chiamata per ogni elemento dellarray:

    • item lelemento.
    • index il suo indice.
    • array larray stesso.

    Se la chiamata ritorna true, la ricerca viene interrotta e viene ritornato item. Se non viene trovato nulla verr ritornato undefined.

    Ad esempio, abbiamo un array di utenti, ognuno con i campi id e name. Cerchiamo quello con id == 1:

    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];
    
    let user = users.find(item => item.id == 1);
    
    alert(user.name); // John

    Gli array di oggetti sono molto comuni, quindi il metodo find risulta molto utile.

    Da notare che nellesempio noi forniamo a find un singolo argomento item => item.id == 1. Gli altri parametri di find sono raramente utilizzati.

    Il metodo arr.findIndex fa essenzialmente la stessa cosa, ma ritorna lindice in cui stata trovata la corrispondenza piuttosto di ritornare loggetto stesso; se loggetto non viene trovato ritorna -1.

    filter

    Il metodo find cerca un singola occorrenza dellelemento, la prima, e se trovata ritorna true.

    Se vogliamo cercare pi occorrenze, possiamo utilizzare arr.filter(fn).

    La sintassi pressoch la stessa di find, ma ritorna un array contenente tutte le corrispondenze trovate:

    let results = arr.filter(function(item, index, array) {
      // se un item  true viene messo dentro results e l'iterazione continua
      // ritorna un array vuoto qualora nessun elemento ritornasse true
    });

    Ad esempio:

    let users = [
      {id: 1, name: "John"},
      {id: 2, name: "Pete"},
      {id: 3, name: "Mary"}
    ];
    
    // ritorna un array con i primi due users
    let someUsers = users.filter(item => item.id < 3);
    
    alert(someUsers.length); // 2

    Trasformare un array

    Questa sezione di occupa dei metodi che trasformano o riordinano gli array.

    map

    Il metodo arr.map uno dei pi utili e maggiormente utilizzati.

    La sintassi :

    let result = arr.map(function(item, index, array) {
      // ritorna il nuovo valore piuttosto di item
    })

    La funzione viene chiamata per ogni elemento dellarray e ritorna un array con i risultati.

    Ad esempio, qui trasformiamo ogni elemento nella propria length:

    let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);
    alert(lengths); // 5,7,6

    sort(fn)

    Il metodo arr.sort ordina larray sul posto, ovvero cambia la posizione originale dei suoi elementi.

    Ritorna altres larray riordinato, ma il risultato viene di solito ignorato, essendo larr originale modificato.

    Ad esempio:

    let arr = [ 1, 2, 15 ];
    
    // il metodo riordina il contenuto di arr (e lo ritorna)
    arr.sort();
    
    alert( arr );  // 1, 15, 2

    Notate qualcosa di strano nel risultato?

    Lordine degli elementi diventato 1, 15, 2. Errato. Ma perch?

    Di default gli elementi vengono ordinati come stringhe.

    Letteralmente, tutti gli elementi vengono convertiti in stringhe e confrontati. Quindi, viene applicato lalgoritmo di ordinamento lessicografico, perci "2" > "15".

    Per utilizzare un ordinamento arbitrario, dobbiamo fornire una funzione come argomento di arr.sort().

    La funzione dovrebbe essere simile a questa:

    function compare(a, b) {
      if (a > b) return 1; // se il primo valore  maggiore del secondo
      if (a == b) return 0; // se i valori sono uguali
      if (a < b) return -1; // se il primo valore  inferiore al secondo
    }

    Ad esempio, per ordinare dei numeri:

    function compareNumeric(a, b) {
      if (a > b) return 1;
      if (a == b) return 0;
      if (a < b) return -1;
    }
    
    let arr = [ 1, 2, 15 ];
    
    arr.sort(compareNumeric);
    
    alert(arr);  // 1, 2, 15

    Ora funziona come dovrebbe.

    Proviamo un attimo a capire cosa sta succedendo. Larray arr pu contenere qualsiasi cosa, giusto? Pu contenere numeri, stringhe, elementi HTML o qualsiasi altra cosa. Abbiamo quindi un insieme di qualcosa. Per poterlo ordinare abbiamo bisogno di una funzione di ordinamento che sappia ordinare gli elementi passati come argomenti. Lordinamento di default di tipo stringa.

    Il metodo arr.sort(fn) implementa un algoritmo di ordinamento. Non dovremmo preoccuparci di come funzioni esattamente (la maggior parte delle volte un quicksort ottimizzato). Questo algoritmo attraverser lintero array e confronter i suoi valori; tutto quello che dobbiamo fare sar fornirgli una funzione fn che esegua il confronto.

    In ogni caso, se mai volessimo conoscere quali elementi vengono comparati nulla ci vieta di utilizzare alert:

    [1, -2, 15, 2, 0, 8].sort(function(a, b) {
      alert( a + " <> " + b );
      return a - b;
    });

    Lalgoritmo potrebbe confrontare un elemento pi volte durante il processo, sebbene tenti di fare il minor numero di confronti possibili.

    Una funzione di confronto pu ritornare qualsiasi numero

    In realt, ad una funzione di confronto solamente richiesto di ritornare un numero positivo per dire maggiore ed uno negativo per dire minore.

    Questo consente di scrivere funzioni pi brevi:

    let arr = [ 1, 2, 15 ];
    
    arr.sort(function(a, b) { return a - b; });
    
    alert(arr);  // 1, 2, 15
    Le arrow functions sono le migliori

    Ricordate le Articolo "function-expressions-arrows" non trovato? Possiamo utilizzarle per pi conciso il codice di ordinamento:

    arr.sort( (a, b) => a - b );

    Questa funziona esattamente come le altre versioni sopra, ma pi breve.

    Use localeCompare for strings

    Ricordate lalgoritmo di comparazione delle strings? Di default, compara le lettere usando il loro codice.

    Per molti alfabeti meglio utilizzare str.localeCompare per ordinare correttamente lettere come .

    Per esempio, ordiniamo alcuni paesi in tedesco:

    let countries = ['sterreich', 'Andorra', 'Vietnam'];
    
    alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, sterreich (wrong)
    
    alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,sterreich,Vietnam (correct!)

    reverse

    Il metodo arr.reverse inverte lordine degli elementi contenuti in arr.

    Ad esempio:

    let arr = [1, 2, 3, 4, 5];
    arr.reverse();
    
    alert( arr ); // 5,4,3,2,1

    Inoltre ritorna arr dopo averlo invertito.

    split e join

    Vediamo una situazione reale. Stiamo scrivendo unapplicazione di messaggistica, e lutente inserisce una lista di destinatari: John, Pete, Mary. Per noi sarebbe pi comodo avere un array di nomi piuttosto di una singola stringa. Come possiamo ottenerlo?

    Il metodo str.split(delim) fa esattamente questo. Divide la stringa in un array utilizzando il delimitatore delim.

    Nellesempio sotto, utilizziamo come delimitatore una virgola seguita da uno spazio:

    let names = 'Bilbo, Gandalf, Nazgul';
    
    let arr = names.split(', ');
    
    for (let name of arr) {
      alert( `A message to ${name}.` ); // A message to Bilbo (e altri name)
    }

    Il metodo split ha un secondo argomento opzionale di tipo numerico un limite di lunghezza per larray. Se questo argomento viene fornito, allora gli elementi extra verranno ignorati. Ma nella pratica raramente utilizzato:

    let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);
    
    alert(arr); // Bilbo, Gandalf
    Split in lettere

    La chiamata a split(s) con largomento vuoto, divider la stringa in un array di lettere:

    let str = "test";
    
    alert( str.split('') ); // t,e,s,t

    La chiamata ad arr.join(separatore) fa esattamente linverso di split. Crea una stringa con gli elementi di arr incollati tra loro dal separatore.

    Ad esempio:

    let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
    
    let str = arr.join(';'); // incolla l'array utilizzando ;
    
    alert( str ); // Bilbo;Gandalf;Nazgul

    reduce/reduceRight

    Quando vogliamo iterare su un array possiamo utilizzare forEach, for o for..of.

    Quando invece abbiamo la necessit di iterare e ritornare dati per ogni elemento possiamo usare map.

    I metodi arr.reduce e arr.reduceRight fanno parte della stessa categoria, ma sono leggermente pi complessi. Vengono utilizzati per calcolare un singolo valore basato sul contenuto dellarray.

    La sintassi :

    let value = arr.reduce(function(accumulator, item, index, array) {
      // ...
    }, [initial]);

    La funzione viene applicata ad ogni elemento dellarray uno dopo laltro, passando il risultato alla chiamata successiva.

    Argomenti:

    • accumulator il risultato della precedente chiamata, uguale ad initial per la prima chiamata (se initial viene fornito).
    • item lattuale elemento dellarray.
    • index la sua posizione.
    • array larray.

    Quando la funzione stata applicata, il risultato viene passato alla chiamata successiva.

    Sembra complicato, ma non lo se pensate al primo argomento come un accumulatore che memorizza il risultato delle precedenti esecuzioni. E alla fine diventa il risultato di reduce.

    Il modo pi semplice per spiegarlo tramite esempi.

    Qui otterremo una somma degli elementi dellarray in una riga:

    let arr = [1, 2, 3, 4, 5];
    
    let result = arr.reduce((sum, current) => sum + current, 0);
    
    alert(result); // 15

    Qui abbiamo utilizzato la variante pi comune di reduce con solo 2 argomenti.

    Proviamo a vedere nel dettaglio cosa succede.

    1. Nella prima esecuzione, sum il valore iniziale (lultimo argomento di reduce), cio 0, e current il primo elemento dellarray, cio 1. Quindi il risultato 1.
    2. Nella seconda esecuzione, sum = 1; gli sommiamo il secondo elemento dellarray(2) e ritorniamo il risultato.
    3. Nella terza esecuzione, sum = 3; gli sommiamo lelemento successivo, e cosi via

    Il flusso di calcolo:

    []

    O nella forma tabellare, in cui ogni riga rappresenta una chiamata di funzione:

    sum current result
    prima chiamata 0 1 1
    seconda chiamata 1 2 3
    terza chiamata 3 3 6
    quarta chiamata 6 4 10
    quinta chiamata 10 5 15

    Come abbiamo potuto osservare, il risultato della chiamata precedente diventa il primo argomento della chiamata successiva.

    Possiamo anche omettere il valore iniziale:

    let arr = [1, 2, 3, 4, 5];
    
    // rimosso il valore iniziale da reduce (no 0)
    let result = arr.reduce((sum, current) => sum + current);
    
    alert( result ); // 15

    Il risultato sarebbe lo stesso. Questo perch se non c un valore iniziale, allora reduce prende il primo elemento dellarray come valore iniziale ed inizia literazione dal secondo elemento.

    La tabella dei calcoli uguale a quella precedente, viene saltata solo la prima riga.

    Questo tipo di utilizzo richiede particolare cura. Se larray vuoto, allora reduce effettua la chiamata senza valore iniziale e provoca un errore.

    Vediamo un esempio:

    let arr = [];
    
    // Errore: Riduzione di un array vuoto senza valore iniziale
    // se il valore iniziale esistesse, reduce lo restituirebbe all'array vuoto.
    arr.reduce((sum, current) => sum + current);

    Quindi fortemente consigliato di specificare sempre un valore iniziale.

    Il metodo arr.reduceRight fa esattamente la stessa cosa, ma da destra verso sinistra.

    Array.isArray

    Gli array non sono un tipo di dato a s del linguaggio. Sono basati sulla sintassi degli oggetti.

    Quindi typeof non aiuta a distinguere un oggetto da un array:

    alert(typeof {}); // object
    alert(typeof []); // lo stesso

    Ma gli array vengono utilizzati cosi spesso che esiste un metodo dedicato per questo: Array.isArray(value). Ritorna true se value un array, false altrimenti.

    alert(Array.isArray({})); // false
    
    alert(Array.isArray([])); // true

    la maggior parte dei metodi accetta thisArg

    Quasi tutti i metodi degli array che richiedono una funzione come find, filter, map, fatta eccezione per sort, accettano un parametro opzionale thisArg.

    Questo parametro non stato spiegato nella sezione sopra, perch viene raramente utilizzato. Studiamolo per completezza.

    Vediamo la sintassi di questi metodi:

    arr.find(func, thisArg);
    arr.filter(func, thisArg);
    arr.map(func, thisArg);
    // ...
    // thisArg  l'ultimo argomento opzionale

    Il valore del parametro thisArg diventa this per func.

    Ad esempio, qui utilizziamo il metodo di un oggetto come filtro e thisArg ci risulta utile:

    let army = {
      minAge: 18,
      maxAge: 27,
      canJoin(user) {
        return user.age >= this.minAge && user.age < this.maxAge;
      }
    };
    
    let users = [
      {age: 16},
      {age: 20},
      {age: 23},
      {age: 30}
    ];
    
    // trova tutti gli users pi giovani di user
    let soldiers = users.filter(army.canJoin, army);
    
    alert(soldiers.length); // 2
    alert(soldiers[0].age); // 20
    alert(soldiers[1].age); // 23

    Nella chiamata sopra, utilizziamo army.canJoin come filtro e forniamo army come contesto. Se non avessimo fornito il contesto, users.filter(army.canJoin) avrebbe chiamato army.canJoin come funzione a s stante, con this=undefined. Che avrebbe provocato un errore.

    Una chiamata a user.filter(army.canJoin, army) pu essere sostituita da users.filter(user => army.canJoin(user)), che fa lo stesso. Lultima versione viene utilizzata pi spesso e per molte persone pi semplice da capire.

    Riepilogo

    Un breve riepilogo dei metodi per array:

    • Per aggiungere/rimuovere elementi:

      • push(...items) aggiunge elementi in coda,
      • pop() estrae un elemento dalla coda,
      • shift() estrae un elemento dalla testa,
      • unshift(...items) aggiunge un elemento alla testa.
      • splice(pos, deleteCount, ...items) allindice pos cancella deleteCount elementi e al loro posto inserisce items.
      • slice(start, end) crea un nuovo array e copia al suo interno gli elementi da start fino ad end (escluso).
      • concat(...items) ritorna un nuovo array: copia tutti gli elementi di quello corrente e ci aggiunge items. Se uno degli items un array, allora vengono presi anche i suoi elementi.
    • Ricercare elementi:

      • indexOf/lastIndexOf(item, pos) cerca item a partire da pos, e ritorna lindice, oppure -1 se non lo trova.
      • includes(value) ritorna true se larray contiene value, altrimenti false.
      • find/filter(func) filtra gli elementi tramite una funzione, ritorna il primo/tutti i valori che ritornano true.
      • findIndex simile a find, ma ritorna lindice piuttosto del valore.
    • Per iterare sugli elementi:

      • forEach(func) invoca func su ogni elemento; non ritorna nulla.
    • Per modificare un array:

      • map(func) crea un nuovo array con i risultati della chiamata func su tutti i suoi elementi.
      • sort(func) ordina larray sul posto, e lo ritorna.
      • reverse() inverte larray sul posto, e lo ritorna.
      • split/join converte una stringa in array e vice versa.
      • reduce/reduceRight(func, initial) calcola un singolo valore chiamando func per ogni elemento e passando un risultato temporaneo tra una chiamata e laltra
    • Un altro metodo utile:

      • Array.isArray(arr) controlla che arr sia un array.

    Da notare che i metodi sort, reverse e splice modificano larray stesso.

    I metodi elencati sono quelli utilizzati pi spesso e sono in grado di coprire il 99% dei casi duso. Ce ne sono altri che possono tornare utili:

    • arr.some(fn)/arr.every(fn) controlla larray.

      La funzione fn viene invocata su ogni elemento dellarray in maniera simile a map. Se qualcuno/tutti i risultati sono true, ritorna true, altrimenti false.

      Questi metodi si comportano quasi come gli operatori || e &&: se fn ritorna un valore vero, arr.some() ritorna immediatamente true e conclude literazione; se fn ritorna un valore falso, arr.every() ritorna immediatamente false e smette di iterare.

      Possiamo utilizzare every per confrontare gli array:

      function arraysEqual(arr1, arr2) {
        return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
      }
      
      alert( arraysEqual([1, 2], [1, 2])); // true
    • arr.fill(value, start, end) riempie larray con value da start fino a end.

    • arr.copyWithin(target, start, end) copia gli elementi da start fino a end dentro se stesso, nella posizione target (sovrascrivendo gli elementi contenuti).

    • arr.flat(depth)/arr.flatMap(fn) crea un nuovo array monodimensionale partendo da un array multidimensionale.

    Per la lista completa, vedere il manuale.

    A prima vista potrebbero sembrare molti metodi da ricordare. Ma in realt molto pi semplice di quanto sembri.

    Tenente sempre un occhio al riassunto fornito sopra. Provate anche a risolvere gli esercizi di questo capitolo.

    In futuro quando avrete bisogno di fare qualcosa con un array, e non saprete come fare tornate qui, guardate il riassunto e trovate il metodo corretto. Gli esempi vi aiuteranno molto. In poco tempo vi risulter naturale ricordare questi metodi, senza troppi sforzi.

    Esercizi

    importanza: 5

    Scrivete una funzione camelize(str) che trasforma le parole separate da un trattino come la-mia-stringa nella notazione a cammello laMiaStringa.

    Quindi: rimuove tutti i trattini; ogni parola dopo un trattino avr una lettera maiuscola.

    Esempi:

    camelize("background-color") == 'backgroundColor';
    camelize("list-style-image") == 'listStyleImage';
    camelize("-webkit-transition") == 'WebkitTransition';

    P.S. Suggerimento: usate split per dividere una stringa in un array, trasformatela e infinite riunite tutto con join.

    Apri una sandbox con i test.

    soluzione
    function camelize(str) {
      return str
        .split('-') // divide 'my-long-word' in un array ['my', 'long', 'word']
        .map(
          // rende maiuscole le prime lettere di tutti gli elementi dell'array eccetto il primo
          // trasforma ['my', 'long', 'word'] in ['my', 'Long', 'Word']
          (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
        )
        .join(''); // unisce ['my', 'Long', 'Word'] in 'myLongWord'
    }

    Apri la soluzione con i test in una sandbox.

    importanza: 4

    Scrivete una funzione filterRange(arr, a, b) che accetta come argomento un array arr, filtra gli elementi tra a e b e ne ritorna un array.

    La funzione non dovrebbe modificare larray. Dovrebbe invece ritornare il nuovo array.

    Ad esempio:

    let arr = [5, 3, 8, 1];
    
    let filtered = filterRange(arr, 1, 4);
    
    alert( filtered ); // 3,1 (i valori filtrati)
    
    alert( arr ); // 5,3,8,1 (non modificato)

    Apri una sandbox con i test.

    soluzione
    function filterRange(arr, a, b) {
      //aggiunte parentesi attorno all'espressione per una migliore leggibilit
      return arr.filter(item => (a <= item && item <= b));
    }
    
    let arr = [5, 3, 8, 1];
    
    let filtered = filterRange(arr, 1, 4);
    
    alert( filtered ); // 3,1 (i valori filtrati)
    
    alert( arr ); // 5,3,8,1 (non modificato)

    Apri la soluzione con i test in una sandbox.

    importanza: 4

    Scrivi una funzione filterRangeInPlace(arr, a, b) che prenda un array arr e ne rimuova tutti i valori, tranne quelli contenuti tra a e b. Il test : a arr[i] b.

    La funzione dovrebbe solamente modificare larray. Senza ritornare nulla.

    Ad esempio:

    let arr = [5, 3, 8, 1];
    
    filterRangeInPlace(arr, 1, 4); // rimuove tutti i numeri tranne quelli da 1 a 4
    
    alert( arr ); // [3, 1]

    Apri una sandbox con i test.

    soluzione
    function filterRangeInPlace(arr, a, b) {
    
      for (let i = 0; i < arr.length; i++) {
        let val = arr[i];
    
        // rimuove se fuori dal range
        if (val < a || val > b) {
          arr.splice(i, 1);
          i--;
        }
      }
    
    }
    
    let arr = [5, 3, 8, 1];
    
    filterRangeInPlace(arr, 1, 4); // rimossi tutti i numeri tranne quelli da 1 a 4
    
    alert( arr ); // [3, 1]

    Apri la soluzione con i test in una sandbox.

    importanza: 4
    let arr = [5, 2, 1, -10, 8];
    
    // ... il tuo codice per ordinare in ordine decrescente
    
    alert( arr ); // 8, 5, 2, 1, -10
    soluzione
    let arr = [5, 2, 1, -10, 8];
    
    arr.sort((a, b) => b - a);
    
    alert( arr );
    importanza: 5

    Abbiamo un array di stringhe arr. Vorremmo ottenerne una sua copia ordinata, mantenenendo arr inalterato.

    Create una funzione copySorted(arr) che ritorni questo tipo di copia.

    let arr = ["HTML", "JavaScript", "CSS"];
    
    let sorted = copySorted(arr);
    
    alert( sorted ); // CSS, HTML, JavaScript
    alert( arr ); // HTML, JavaScript, CSS (nessuna modifica)
    soluzione

    Possiamo utilizzare slice() per fare una copia e solo dopo riordinarla:

    function copySorted(arr) {
      return arr.slice().sort();
    }
    
    let arr = ["HTML", "JavaScript", "CSS"];
    
    let sorted = copySorted(arr);
    
    alert( sorted );
    alert( arr );
    importanza: 5

    Create un costruttore Calculator che crei oggetti calcoltrice estensibili.

    Il compito consiste in due parti.

    1. La prima parte consiste nellimplementare il metodo calculate(str) che accetti una stringa come "1 + 2" nel formato NUMERO operatore NUMERO (delimitata da spazi) e ne ritorni il risultato. Dovrebbe saper interpretare sia + che -.

      Esempio duso:

      let calc = new Calculator;
      
      alert( calc.calculate("3 + 7") ); // 10
    2. Successivamente aggiungete il metodo addMethod(name, func) che ha lo scopo di insegnare alla calcolatrice una nuova operazione. Questo prende il nome delloperatore name e i due argomenti della funzione func(a,b) che lo implementa.

      Ad esempio, proviamo ad aggiungere la moltiplicazione *, divisione / e la potenza **:

      let powerCalc = new Calculator;
      powerCalc.addMethod("*", (a, b) => a * b);
      powerCalc.addMethod("/", (a, b) => a / b);
      powerCalc.addMethod("**", (a, b) => a ** b);
      
      let result = powerCalc.calculate("2 ** 3");
      alert( result ); // 8
    • Non richiesta la gestione delle parentesi o di operazioni complesse.
    • I numeri e loperatore sono separati esattamente da un singolo spazio.
    • Se ne hai voglia potresti provare ad aggiungere un minimo di gestione degli errori.

    Apri una sandbox con i test.

    soluzione
    • Da notare come vengono memorizzati i metodi. Vengono semplicemente aggiunti allinterno delloggetto.
    • Tutti i test e le conversioni numeriche vegono effettuati nel metodo calculate. In futuro potrebbe essere esteso per supportare espressioni molto pi complesse.
    function Calculator() {
    
      this.methods = {
        "-": (a, b) => a - b,
        "+": (a, b) => a + b
      };
    
      this.calculate = function(str) {
    
        let split = str.split(' '),
          a = +split[0],
          op = split[1],
          b = +split[2];
    
        if (!this.methods[op] || isNaN(a) || isNaN(b)) {
          return NaN;
        }
    
        return this.methods[op](a, b);
      };
    
      this.addMethod = function(name, func) {
        this.methods[name] = func;
      };
    }

    Apri la soluzione con i test in una sandbox.

    importanza: 5

    Avete un array di oggetti user; ognuno di essi ha la propriet user.name. Scrivete il codice che converte gli oggetti in un array di nomi.

    Ad esempio:

    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 28 };
    
    let users = [ john, pete, mary ];
    
    let names = /* ... il vostro codice */
    
    alert( names ); // John, Pete, Mary
    soluzione
    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 28 };
    
    let users = [ john, pete, mary ];
    
    let names = users.map(item => item.name);
    
    alert( names ); // John, Pete, Mary
    importanza: 5

    Avete un array di oggetti user, ognuno di questi possiede name, surname e id.

    Scrivete il codice per creare un altro array che derivi da questo, sempre composto da oggetti con id e fullName, dove fullName viene generato da name e surname.

    Un esempio:

    let john = { name: "John", surname: "Smith", id: 1 };
    let pete = { name: "Pete", surname: "Hunt", id: 2 };
    let mary = { name: "Mary", surname: "Key", id: 3 };
    
    let users = [ john, pete, mary ];
    
    let usersMapped = /* ... il vostro codice ... */
    
    /*
    usersMapped = [
      { fullName: "John Smith", id: 1 },
      { fullName: "Pete Hunt", id: 2 },
      { fullName: "Mary Key", id: 3 }
    ]
    */
    
    alert( usersMapped[0].id ) // 1
    alert( usersMapped[0].fullName ) // John Smith

    Quindi, in realt avrete bisogno di mappare un array di oggetti in un altro. Provate ad utilizzare =>.

    soluzione
    let john = { name: "John", surname: "Smith", id: 1 };
    let pete = { name: "Pete", surname: "Hunt", id: 2 };
    let mary = { name: "Mary", surname: "Key", id: 3 };
    
    let users = [ john, pete, mary ];
    
    let usersMapped = users.map(user => ({
      fullName: `${user.name} ${user.surname}`,
      id: user.id
    }));
    
    /*
    usersMapped = [
      { fullName: "John Smith", id: 1 },
      { fullName: "Pete Hunt", id: 2 },
      { fullName: "Mary Key", id: 3 }
    ]
    */
    
    alert( usersMapped[0].id ); // 1
    alert( usersMapped[0].fullName ); // John Smith

    Da notare che nellarrow function abbiamo bisogno di utilizzare unulteriore parentesi.

    Non possiamo scrivere semplicemente:

    let usersMapped = users.map(user => {
      fullName: `${user.name} ${user.surname}`,
      id: user.id
    });

    Se ricordate, ci sono due tipi di arrow function: senza corpo value => expr e con il corpo value => {...}.

    Qui JavaScript tratter { come linzio del corpo della funzione, non linizio delloggetto. Questo trucco viene utilizzato per racchiuderle nelle normali parentesi:

    let usersMapped = users.map(user => ({
      fullName: `${user.name} ${user.surname}`,
      id: user.id
    }));

    Ora funziona.

    importanza: 5

    Scrivete una funzione sortByAge(users) che accetti un array di oggetti con propriet age e lo riordini per age.

    Ad esempio:

    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 28 };
    
    let arr = [ pete, john, mary ];
    
    sortByAge(arr);
    
    // ora: [john, mary, pete]
    alert(arr[0].name); // John
    alert(arr[1].name); // Mary
    alert(arr[2].name); // Pete
    soluzione
    function sortByAge(arr) {
      arr.sort((a, b) => a.age - b.age);
    }
    
    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 28 };
    
    let arr = [ pete, john, mary ];
    
    sortByAge(arr);
    
    // ordinato: [john, mary, pete]
    alert(arr[0].name); // John
    alert(arr[1].name); // Mary
    alert(arr[2].name); // Pete
    importanza: 3

    Scrivete una funzione shuffle(array) che rimescoli (riordini casualmente) gli elementi di un array.

    Esecuzioni multiple di shuffle dovrebbero portare a diversi ordinamenti degli elementi. Ad esempio:

    let arr = [1, 2, 3];
    
    shuffle(arr);
    // arr = [3, 2, 1]
    
    shuffle(arr);
    // arr = [2, 1, 3]
    
    shuffle(arr);
    // arr = [3, 1, 2]
    // ...

    Tutti gli elementi ordinati dovrebbero avere una probabilit identica. Ad esempio, [1,2,3] pu essere riordinato come [1,2,3] o [1,3,2] o [3,1,2] etc; ognuno dei casi deve avere la stessa probabilit.

    soluzione

    La soluzione pi semplice potrebbe essere:

    function shuffle(array) {
      array.sort(() => Math.random() - 0.5);
    }
    
    let arr = [1, 2, 3];
    shuffle(arr);
    alert(arr);

    Questa in qualche modo funziona, perch Math.random() - 0.5 un numero casuale che pu essere sia positivo che negativo, quindi la funzione riordina gli elementi casualmente.

    Con questa funzione di ordinamento, non tutte le permutazioni hanno la stessa probabilit.

    Ad esempio, considerando il codice sotto. Esegue shuffle 1000000 di volte e conta il numero di occorrenze di tutti i risultati possibili:

    function shuffle(array) {
      array.sort(() => Math.random() - 0.5);
    }
    
    // counts of appearances for all possible permutations
    let count = {
      '123': 0,
      '132': 0,
      '213': 0,
      '231': 0,
      '321': 0,
      '312': 0
    };
    
    for (let i = 0; i < 1000000; i++) {
      let array = [1, 2, 3];
      shuffle(array);
      count[array.join('')]++;
    }
    
    // show counts of all possible permutations
    for (let key in count) {
      alert(`${key}: ${count[key]}`);
    }

    Un esempio di risultato possibile (dipende dal motore JS):

    123: 250706
    132: 124425
    213: 249618
    231: 124880
    312: 125148
    321: 125223

    Possiamo chiaramente vedere che le combinazioni 123 e 213 appaiono molto pi spesso delle altre.

    Il risultato del codice potrebbe variare in base al motore JavaScript, ma gi possiamo notare che questo tipo di approccio inaccettabile.

    Perch non funziona? Generalmente parlando, sort una scatola nera: gli passiamo un array ed una funzione di confronto e ci aspettiamo di ottenere larray ordinato. Ma a causa della difficolt nellimplementazione della casualit la scatola nera potrebbe funzionare male; quanto male, dipende dal motore JavaScript.

    Esistono altri modi per questo compito. Ad esempio, c un ottimo algoritmo chiamato Fisher-Yates shuffle. Lidea di attraversare larray in ordine inverso e di scambiare lelemento con un altro casuale, che venga prima di lui:

    function shuffle(array) {
      for (let i = array.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1)); // indice casuale da 0 a i
    
        //scambia gli elementi array[i] e array[j]
        // usiamo la sintassi "destructuring assignment"
        //troverai maggiori dettagli su questa sintassi nei capitoli seguenti
        //potrebbe essere scritto come
        // let t = array[i]; array[i] = array[j]; array[j] = t
        [array[i], array[j]] = [array[j], array[i]];
      }
    }

    Proviamo ad eseguire lo stesso test:

    function shuffle(array) {
      for (let i = array.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
      }
    }
    
    // counts of appearances for all possible permutations
    let count = {
      '123': 0,
      '132': 0,
      '213': 0,
      '231': 0,
      '321': 0,
      '312': 0
    };
    
    for (let i = 0; i < 1000000; i++) {
      let array = [1, 2, 3];
      shuffle(array);
      count[array.join('')]++;
    }
    
    // show counts of all possible permutations
    for (let key in count) {
      alert(`${key}: ${count[key]}`);
    }

    Un possibile risultato:

    123: 166693
    132: 166647
    213: 166628
    231: 167517
    312: 166199
    321: 166316

    Ora sembra funzionare: tutte lo occorrenze appaiono con la stessa probabilit.

    Inoltre, anche le performance dellalgoritmo Fisher-Yates sono migliori, poich non richiesto alcun riordinamento.

    importanza: 4

    Scrivete una funzione getAverageAge(users) che accetti un array di oggetti con la propriet age e ritorni let media.

    La formula della media : (age1 + age2 + ... + ageN) / N.

    Ad esempio:

    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 29 };
    
    let arr = [ john, pete, mary ];
    
    alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28
    soluzione
    function getAverageAge(users) {
      return users.reduce((prev, user) => prev + user.age, 0) / users.length;
    }
    
    let john = { name: "John", age: 25 };
    let pete = { name: "Pete", age: 30 };
    let mary = { name: "Mary", age: 29 };
    
    let arr = [ john, pete, mary ];
    
    alert( getAverageAge(arr) ); // 28
    importanza: 4

    Abbiamo un array arr.

    Create una funzione unique(arr) che ritorni un array con elementi unici.

    Ad esempio:

    function unique(arr) {
      /* your code */
    }
    
    let strings = ["Hare", "Krishna", "Hare", "Krishna",
      "Krishna", "Krishna", "Hare", "Hare", ":-O"
    ];
    
    alert( unique(strings) ); // Hare, Krishna, :-O

    Apri una sandbox con i test.

    soluzione

    Attraversiamo gli elementi dellarray:

    • Per ogni elemento controlliamo se larray risultante gi lo contiene.
    • Se lo troviamo, passiamo al prossimo, altrimenti lo aggiungiamo.
    function unique(arr) {
      let result = [];
    
      for (let str of arr) {
        if (!result.includes(str)) {
          result.push(str);
        }
      }
    
      return result;
    }
    
    let strings = ["Hare", "Krishna", "Hare", "Krishna",
      "Krishna", "Krishna", "Hare", "Hare", ":-O"
    ];
    
    alert( unique(strings) ); // Hare, Krishna, :-O

    Il codice funziona, ma c un potenziale problema di performace.

    Il metodo result.includes(str) internamente attraversa larray result e confronta ogni elemento con str per trovare una corrispondenza.

    Quindi se ci sono 100 elementi in result e nessuna corrispondenza con str, attraverseremo lintero array result eseguendo essattamente 100 confronti. Se larray result grande, ad esempio 10000, ci sarebbero 10000 di confronti.

    Non propriamente un problema, perch il motore JavaScript molto rapido, quindi un array grande 10000 questione di pochi microsecondi.

    Ma dovremo eseguire questo test per ogni elemento di arr nel ciclo for.

    Quindi se arr.length 10000 avremmo qualcosa come 10000*10000 = 100 milioni di confronti. Sono molti.

    Quindi la soluzione funziona bene solo con array di piccola taglia.

    Pi avanti nel capitolo Map e Set vedremo come ottimizare questo metodo.

    Apri la soluzione con i test in una sandbox.

    importanza: 4

    Immaginiamo di ricevere un array di utenti nella forma {id:..., name:..., age... }.

    Scrivi una funzione groupById(arr) che ricavi un oggetto da esso, con id come chiave e gli elementi dellarray come valori

    Ad esempio:

    let users = [
      {id: 'john', name: "John Smith", age: 20},
      {id: 'ann', name: "Ann Smith", age: 24},
      {id: 'pete', name: "Pete Peterson", age: 31},
    ];
    
    let usersById = groupById(users);
    
    /*
    // dopo la chiamata dovremmo avere:
    
    usersById = {
      john: {id: 'john', name: "John Smith", age: 20},
      ann: {id: 'ann', name: "Ann Smith", age: 24},
      pete: {id: 'pete', name: "Pete Peterson", age: 31},
    }
    */

    Una funzione simile molto utile quando si lavora con dati provenienti da un server.

    In questo esercizio sappiamo che id unico. Non ci saranno due array con lo stesso id.

    Per favore utilizza il metodo .reduce nella soluzione.

    Apri una sandbox con i test.

    soluzione
    function groupById(array) {
      return array.reduce((obj, value) => {
        obj[value.id] = value;
        return obj;
      }, {})
    }

    Apri la soluzione con i test in una sandbox.

    Mappa del tutorial

    Commenti

    leggi questo prima di lasciare un commento
    • Per qualsiasi suggerimento - per favore, apri una issue su GitHub o una pull request, piuttosto di lasciare un commento.
    • Se non riesci a comprendere quanto scitto nell'articolo ti preghiamo di fornire una spiegazione chiara.
    • Per inserire delle righe di codice utilizza il tag <code>, per molte righe includile nel tag <pre>, per pi di 10 righe utilizza una sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page