JavaScript Array: copie, aggiornamenti immutabili e iterazioni sicure

by Anastasia P.
SHARE
JavaScript Array: copie, aggiornamenti immutabili e iterazioni sicure
© Guida-HTML5.it

Introduzione

Quando si lavora con gli Array in JavaScript, una delle difficoltà più comuni non riguarda la creazione o la lettura dei dati, ma il modo in cui questi vengono modificati. Metodi come push(), pop() e splice() cambiano direttamente l’array originale. Questo comportamento è chiamato mutazione.

La mutazione non è sempre un errore, ma può generare problemi quando lo stesso array viene utilizzato in più parti dell’applicazione. Un componente dell’interfaccia, una funzione di validazione e una funzione di calcolo potrebbero condividere lo stesso riferimento. Se una di queste funzioni modifica l’array, anche le altre vedranno dati diversi da quelli iniziali.

In questo tutorial vedremo come:

  • creare array in modo chiaro;
  • distinguere tra riferimento, copia superficiale e copia profonda;
  • aggiornare array senza modificarli direttamente;
  • usare metodi di iterazione per produrre nuovi risultati;
  • gestire un piccolo elenco di attività in modo sicuro e prevedibile.

Codice completo

// Elenco iniziale delle attività
const attivita = [
  { id: 1, titolo: "Studiare JavaScript", completata: false },
  { id: 2, titolo: "Scrivere gli esercizi", completata: true },
  { id: 3, titolo: "Ripassare gli Array", completata: false }
];

// 1. Creiamo un nuovo array senza modificare quello originale
const nuovaAttivita = {
  id: 4,
  titolo: "Leggere la documentazione",
  completata: false
};

const elencoAggiornato = [...attivita, nuovaAttivita];

console.log("Array originale:", attivita);
console.log("Array aggiornato:", elencoAggiornato);

// 2. Aggiorniamo una sola attività con map()
const elencoCompletato = elencoAggiornato.map(attivita => {
  if (attivita.id === 1) {
    return {
      ...attivita,
      completata: true
    };
  }

  return attivita;
});

// 3. Eliminiamo un elemento con filter()
const elencoSenzaSecondaAttivita = elencoCompletato.filter(
  attivita => attivita.id !== 2
);

// 4. Cerchiamo un elemento con find()
const primaAttivitaNonCompletata = elencoSenzaSecondaAttivita.find(
  attivita => !attivita.completata
);

// 5. Calcoliamo un riepilogo con reduce()
const riepilogo = elencoSenzaSecondaAttivita.reduce(
  (risultato, attivita) => {
    risultato.totale++;

    if (attivita.completata) {
      risultato.completate++;
    } else {
      risultato.daCompletare++;
    }

    return risultato;
  },
  {
    totale: 0,
    completate: 0,
    daCompletare: 0
  }
);

console.log("Dopo l´aggiornamento:", elencoCompletato);
console.log("Dopo la rimozione:", elencoSenzaSecondaAttivita);
console.log("Prima attività non completata:", primaAttivitaNonCompletata);
console.log("Riepilogo:", riepilogo);

Spiegazione

Riferimenti e copie degli array

Un array assegnato a una nuova variabile non viene copiato automaticamente. Le due variabili fanno riferimento allo stesso oggetto in memoria:

const numeri = [1, 2, 3];
const altroRiferimento = numeri;

altroRiferimento.push(4);

console.log(numeri); // [1, 2, 3, 4]

Per creare un nuovo array è possibile usare l’operatore spread:

const numeri = [1, 2, 3];
const copia = [...numeri];

copia.push(4);

console.log(numeri); // [1, 2, 3]
console.log(copia);  // [1, 2, 3, 4]

Questa tecnica crea una copia superficiale. Gli elementi primitivi, come numeri e stringhe, vengono copiati correttamente. Se però l’array contiene oggetti, gli oggetti interni mantengono il proprio riferimento.

const utenti = [
  { nome: "Anna", attivo: true }
];

const copiaUtenti = [...utenti];
copiaUtenti[0].attivo = false;

console.log(utenti[0].attivo); // false

Per aggiornare un oggetto senza modificare quello originale, è necessario copiare anche l’oggetto interessato. Il metodo map() è ideale perché visita ogni elemento e restituisce un nuovo array.

const utentiAggiornati = utenti.map(utente => {
  return {
    ...utente,
    attivo: true
  };
});

Aggiungere, aggiornare e rimuovere elementi

Per aggiungere un elemento alla fine senza usare push(), si può creare un nuovo array con spread:

const numeri = [10, 20];
const risultato = [...numeri, 30];

Per aggiungere un elemento all’inizio è sufficiente invertire l’ordine:

const risultato = [5, ...numeri];

Per rimuovere elementi senza splice(), si usa filter(). Il callback deve restituire true per conservare un elemento e false per eliminarlo dal nuovo array.

const prodotti = [
  { id: 1, nome: "Mouse" },
  { id: 2, nome: "Tastiera" }
];

const senzaMouse = prodotti.filter(prodotto => prodotto.id !== 1);

Iterare per ottenere informazioni

Il metodo find() restituisce il primo elemento che soddisfa una condizione, oppure undefined se non trova risultati. È utile quando si cerca un singolo record.

Il metodo reduce(), invece, permette di trasformare molti elementi in un unico valore: un numero, una stringa, un oggetto o anche un altro array. Nell’esempio, viene creato un oggetto riepilogativo con il conteggio delle attività.

Rispetto a una serie di modifiche distribuite nel codice, questa tecnica rende il flusso dei dati più facile da controllare: ogni operazione riceve un array e restituisce un nuovo risultato.

Best practice

  • Preferisci const: impedisce di riassegnare la variabile, pur permettendo modifiche interne all’array. Per evitare anche le mutazioni, usa metodi che restituiscono nuovi array.
  • Usa spread per le copie semplici: [...array] è leggibile e adatto ad array di valori primitivi.
  • Ricorda la copia superficiale: con array di oggetti, copia anche l’oggetto modificato usando { ...oggetto }.
  • Scegli il metodo in base all’obiettivo: map() per trasformare, filter() per selezionare, find() per cercare un elemento e reduce() per aggregare.
  • Evita callback con effetti collaterali: una funzione passata a map() dovrebbe produrre un nuovo valore, non modificare dati esterni.
  • Controlla i casi vuoti: find() può restituire undefined e reduce() dovrebbe avere un valore iniziale esplicito.
  • Usa structuredClone() con attenzione: può creare copie profonde di strutture compatibili, ma non è sempre necessario e non sostituisce una progettazione corretta dei dati.

Riepilogo

Gli array JavaScript sono strutture flessibili, ma la loro mutabilità richiede attenzione. Assegnare un array a una nuova variabile crea un riferimento, non una copia. Per produrre un nuovo array si può usare lo spread oppure un metodo di iterazione come map() o filter().

Un approccio immutabile consiste nel non modificare direttamente i dati esistenti: si crea invece una nuova versione dell’array. Questo stile è particolarmente utile nelle applicazioni web, nei sistemi con stato condiviso e nei test automatici, perché rende più prevedibili le trasformazioni.

La regola pratica è semplice: usa map() per aggiornare elementi, filter() per rimuoverli, spread per aggiungerli e reduce() per creare riepiloghi. Quando gli elementi sono oggetti, copia anche l’oggetto che stai modificando.

Approfondisci con risorse ufficiali

SHARE