Appiattire un Array in JavaScript

Concetti Chiave
  • In JavaScript, possiamo appiattire un array multidimensionale utilizzando il metodo flat(), che restituisce un nuovo array con tutti gli elementi degli array annidati.
  • Il metodo flat() appiattisce un solo livello di annidamento per impostazione, ma possiamo specificare un argomento per appiattire più livelli.
  • Il metodo flatMap() combina la funzionalità di map() e flat(), permettendo di applicare una funzione a ogni elemento e appiattire il risultato in un unico livello.
  • Possiamo utilizzare flatMap() per trasformare e appiattire gli array in un'unica operazione, ad esempio per calcolare il quadrato di ogni numero in un array multidimensionale.

Appiattire un Array: flat

Quando abbiamo a che fare con un array multidimensionale, che in Javascript si traduce in un array di array, può essere utile appiattire l'array, in inglese flattening.

L'operazione di appiattimento consiste nel prendere un array che contiene altri array e restituire un nuovo array che contiene tutti gli elementi degli array annidati, in un unico livello.

Per intenderci, supponiamo di avere un array come questo:

let multilivello = [
    [1, 2, 3],
    [4, 5],
    [6, 7, 8]
];

Si tratta di un array di array, composto da 3 array annidati e di lunghezza differente. Se vogliamo appiattirlo in un array a un solo livello, il risultato sarà:

let appiattito = [1, 2, 3, 4, 5, 6, 7, 8];

In ES2019, il metodo flat() crea e restituisce un nuovo array che contiene gli stessi elementi dell'array su cui è chiamato, tranne che qualsiasi elemento che sia esso stesso un array viene "appiattito" nell'array restituito.

Riprendendo l'esempio precedente, possiamo appiattire l'array multilivello in questo modo:

let appiattito = multilivello.flat();
console.log(appiattito); // => [1, 2, 3, 4, 5, 6, 7, 8]

Quando chiamato senza argomenti, flat() appiattisce un solo livello di annidamento. Gli elementi dell'array originale che sono essi stessi array vengono appiattiti, ma gli elementi array dei sotto-array non vengono appiattiti.

Ad esempio, supponiamo di avere un array con ben due livelli di annidamento:

let multi = [
    [1, 2, 3],
    [
        [4, 5],
        [6, 7, 8]
    ],
    [9, 10]
];

Se applichiamo flat() a questo array, otterremo:

let appiattito = multi.flat();
console.log(appiattito);

Il risultato sarà:

[1, 2, 3, [4, 5], [6, 7, 8], 9, 10]

Ossia, l'array è stato appiattito di un solo livello, quindi gli array [4, 5] e [6, 7, 8] sono rimasti annidati.

Se vogliamo appiattire più livelli, dobbiamo passare un argomento a flat(), specificando il numero di livelli da appiattire. Ad esempio, per appiattire completamente l'array multi, possiamo usare:

let appiattito = multi.flat(2);
console.log(appiattito);

Il risultato sarà:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Ossia, l'array è stato completamente appiattito, quindi tutti gli elementi sono ora a un solo livello.

flatMap: appiattire e mappare

Il metodo flatMap() funziona proprio come il metodo map() tranne per il fatto che l'array restituito viene automaticamente appiattito come se fosse passato a flat(). Cioè, chiamare a.flatMap(f) è equivalente (ma più efficiente di) a.map(f).flat().

Ad esempio, supponiamo di avere il seguente array:

let multilivello = [
    [1, 2, 3],
    [4, 5],
    [6, 7, 8]
];

Vogliamo appiattire questo array e, allo stesso tempo, calcolare il quadrato di ogni numero. Possiamo farlo con flatMap() in questo modo:

let quadrati = multilivello.flatMap(sottolivello => sottolivello.map(x => x ** 2));
console.log(quadrati); // => [1, 4, 9, 16, 25, 36, 49, 64]

Analogamente, supponiamo di avere un array di frasi e vogliamo ottenere un array di parole. Possiamo usare flatMap() per dividere ogni frase in parole e appiattire il risultato:

let frasi = ["Ciao, come stai?", "Tutto bene, grazie!"];
let parole = frasi.flatMap(frase => frase.split(" "));
console.log(parole); // => ["Ciao,", "come", "stai?", "Tutto", "bene,", "grazie!"];

Possiamo pensare a flatMap() come una generalizzazione di map() che permette a ogni elemento dell'array di input di mappare a qualsiasi numero di elementi dell'array di output. In particolare, flatMap() consente di mappare elementi di input a un array vuoto, che si appiattisce a nulla (quindi non aggiunge elementi) nell'array di output:

// Mappa numeri non-negativi alle loro radici quadrate
let origine = [-2, -1, 1, 2];

let risultato = origine.flatMap(x => x < 0 ? [] : Math.sqrt(x))

console.log(risultato); // => [1, 1.4142135623730951]