Vai al contenuto

Map, filter e reduce

Array methods avanzati per programmazione funzionale moderna.

Programmazione Funzionale con Array

I metodi map, filter e reduce sono i pilastri della programmazione funzionale in JavaScript. Permettono di trasformare, filtrare e aggregare dati in modo elegante e dichiarativo.

Perché Sono Importanti?

  • Leggibilità → Codice più chiaro e espressivo
  • Immutabilità → Non modificano l'array originale
  • Composizione → Possono essere concatenati
  • Standard moderno → Usati in React, Vue, Angular...

🎯 In Questa Lezione

  • ✅ map() - Trasforma ogni elemento
  • ✅ filter() - Filtra elementi per condizione
  • ✅ reduce() - Aggrega array in singolo valore
  • ✅ find(), findIndex(), some(), every() - Metodi utility
  • ✅ Chaining - Concatenare metodi
  • ✅ Best Practices - Quando usare quale metodo

map() - Trasforma Array

map() crea un nuovo array applicando una funzione a ogni elemento. L'array risultante ha la stessa lunghezza dell'originale.

  1. Sintassi e Uso Base

map() Base

// Sintassi: array.map(callback(elemento, indice, array))

const numeri = [1, 2, 3, 4, 5];

// Con for classico (VECCHIO MODO ❌)
let doppi = [];
for (let i = 0; i < numeri.length; i++) {
    doppi.push(numeri[i] * 2);
}
console.log(doppi);  // [2, 4, 6, 8, 10]

// ✨ Con map (MODERNO ✅)
const doppiMap = numeri.map(function(numero) {
    return numero * 2;
});
console.log(doppiMap);  // [2, 4, 6, 8, 10]

// ✨ Con arrow function (CONSIGLIATO 🌟)
const doppiArrow = numeri.map(n => n * 2);
console.log(doppiArrow);  // [2, 4, 6, 8, 10]

// Array originale NON modificato
console.log(numeri);  // [1, 2, 3, 4, 5]
  1. Esempi Pratici map()

map() Esempi

// Estrai proprietà da array di oggetti
const utenti = [
    {id: 1, nome: "Mario", età: 30},
    {id: 2, nome: "Anna", età: 25},
    {id: 3, nome: "Luca", età: 35}
];

const nomi = utenti.map(utente => utente.nome);
console.log(nomi);  // ["Mario", "Anna", "Luca"]

const età = utenti.map(u => u.età);
console.log(età);  // [30, 25, 35]

// Trasforma stringhe
const frutti = ["mela", "banana", "arancia"];
const maiuscole = frutti.map(f => f.toUpperCase());
console.log(maiuscole);  // ["MELA", "BANANA", "ARANCIA"]

// Crea nuovi oggetti
const prodotti = [
    {nome: "Laptop", prezzo: 999},
    {nome: "Mouse", prezzo: 25},
    {nome: "Tastiera", prezzo: 75}
];

const conSconto = prodotti.map(p => ({
    ...p,
    prezzoOriginale: p.prezzo,
    prezzo: p.prezzo * 0.8,  // 20% sconto
    sconto: "20%"
}));

console.log(conSconto);
// [{nome: "Laptop", prezzo: 799.2, prezzoOriginale: 999, sconto: "20%"}, ...]

// Con indice
const lettere = ["a", "b", "c"];
const conIndice = lettere.map((lettera, indice) => {
    return `${indice + 1}. ${lettera.toUpperCase()}`;
});
console.log(conIndice);  // ["1. A", "2. B", "3. C"]

💡 Quando Usare map()

Usa map() quando vuoi:

  • Trasformare ogni elemento dell'array
  • Estrarre proprietà da oggetti
  • Convertire formati (°C → °F, km → miglia...)
  • Creare componenti React da dati (molto comune!)

filter() - Filtra Array

filter() crea un nuovo array contenendo solo gli elementi che superano una condizione. La lunghezza può essere diversa dall'originale.

  1. Sintassi e Uso Base

filter() Base

// Sintassi: array.filter(callback(elemento, indice, array))

const numeri = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Con for classico (VECCHIO MODO ❌)
let pari = [];
for (let i = 0; i < numeri.length; i++) {
    if (numeri[i] % 2 === 0) {
        pari.push(numeri[i]);
    }
}
console.log(pari);  // [2, 4, 6, 8, 10]

// ✨ Con filter (MODERNO ✅)
const pariFilter = numeri.filter(function(numero) {
    return numero % 2 === 0;
});
console.log(pariFilter);  // [2, 4, 6, 8, 10]

// ✨ Con arrow function (CONSIGLIATO 🌟)
const pariArrow = numeri.filter(n => n % 2 === 0);
console.log(pariArrow);  // [2, 4, 6, 8, 10]

// Filtra dispari
const dispari = numeri.filter(n => n % 2 !== 0);
console.log(dispari);  // [1, 3, 5, 7, 9]

// Maggiori di 5
const maggiori = numeri.filter(n => n > 5);
console.log(maggiori);  // [6, 7, 8, 9, 10]
  1. Esempi Pratici filter()

filter() Esempi

// Filtra oggetti per proprietà
const prodotti = [
    {nome: "Laptop", prezzo: 999, disponibile: true},
    {nome: "Mouse", prezzo: 25, disponibile: true},
    {nome: "Monitor", prezzo: 300, disponibile: false},
    {nome: "Tastiera", prezzo: 75, disponibile: true}
];

// Solo prodotti disponibili
const disponibili = prodotti.filter(p => p.disponibile);
console.log(disponibili);
// [{nome: "Laptop", ...}, {nome: "Mouse", ...}, {nome: "Tastiera", ...}]

// Prodotti economici (< €100)
const economici = prodotti.filter(p => p.prezzo < 100);
console.log(economici);
// [{nome: "Mouse", ...}, {nome: "Tastiera", ...}]

// Filtra stringhe
const parole = ["ciao", "a", "mondo", "hi", "JavaScript"];
const lunghe = parole.filter(p => p.length > 3);
console.log(lunghe);  // ["ciao", "mondo", "JavaScript"]

// Rimuovi duplicati (uso avanzato)
const numeriDuplicati = [1, 2, 2, 3, 4, 4, 5];
const unici = numeriDuplicati.filter((n, indice, array) => {
    return array.indexOf(n) === indice;
});
console.log(unici);  // [1, 2, 3, 4, 5]

// Filtra per età
const persone = [
    {nome: "Mario", età: 17},
    {nome: "Anna", età: 25},
    {nome: "Luca", età: 15},
    {nome: "Sara", età: 30}
];

const maggiorenni = persone.filter(p => p.età >= 18);
console.log(maggiorenni);
// [{nome: "Anna", età: 25}, {nome: "Sara", età: 30}]

💡 Quando Usare filter()

Usa filter() quando vuoi:

  • Trovare elementi che soddisfano una condizione
  • Rimuovere elementi indesiderati
  • Creare subset di dati
  • Implementare funzionalità di ricerca/filtro

reduce() - Aggrega Array

reduce() "riduce" un array a un singolo valore applicando una funzione cumulativa. Il più potente e flessibile!

  1. Sintassi e Uso Base

reduce() Base

// Sintassi: array.reduce(callback(accumulatore, elemento, indice, array), valoreIniziale)

const numeri = [1, 2, 3, 4, 5];

// Con for classico (VECCHIO MODO ❌)
let somma = 0;
for (let i = 0; i < numeri.length; i++) {
    somma += numeri[i];
}
console.log(somma);  // 15

// ✨ Con reduce (MODERNO ✅)
const sommaReduce = numeri.reduce(function(acc, num) {
    return acc + num;
}, 0);  // 0 è il valore iniziale
console.log(sommaReduce);  // 15

// ✨ Con arrow function (CONSIGLIATO 🌟)
const sommaArrow = numeri.reduce((acc, num) => acc + num, 0);
console.log(sommaArrow);  // 15

// Come funziona step-by-step:
// Passo 1: acc = 0 (valore iniziale), num = 1 → return 0 + 1 = 1
// Passo 2: acc = 1, num = 2 → return 1 + 2 = 3
// Passo 3: acc = 3, num = 3 → return 3 + 3 = 6
// Passo 4: acc = 6, num = 4 → return 6 + 4 = 10
// Passo 5: acc = 10, num = 5 → return 10 + 5 = 15
// Risultato finale: 15
  1. Esempi Pratici reduce()

reduce() Esempi

const numeri = [10, 5, 8, 3, 15];

// Somma
const somma = numeri.reduce((acc, n) => acc + n, 0);
console.log(somma);  // 41

// Prodotto
const prodotto = numeri.reduce((acc, n) => acc * n, 1);
console.log(prodotto);  // 18000

// Massimo
const max = numeri.reduce((acc, n) => n > acc ? n : acc);
console.log(max);  // 15

// Minimo
const min = numeri.reduce((acc, n) => n < acc ? n : acc);
console.log(min);  // 3

// Media
const media = numeri.reduce((acc, n, i, arr) => {
    acc += n;
    if (i === arr.length - 1) {
        return acc / arr.length;
    }
    return acc;
}, 0);
console.log(media);  // 8.2

// Conta occorrenze
const frutti = ["mela", "banana", "mela", "arancia", "banana", "mela"];
const conteggio = frutti.reduce((acc, frutto) => {
    acc[frutto] = (acc[frutto] || 0) + 1;
    return acc;
}, {});
console.log(conteggio);
// {mela: 3, banana: 2, arancia: 1}

// Raggruppa per proprietà
const persone = [
    {nome: "Mario", età: 25, città: "Roma"},
    {nome: "Anna", età: 30, città: "Milano"},
    {nome: "Luca", età: 25, città: "Roma"},
    {nome: "Sara", età: 30, città: "Milano"}
];

const perCittà = persone.reduce((acc, persona) => {
    if (!acc[persona.città]) {
        acc[persona.città] = [];
    }
    acc[persona.città].push(persona);
    return acc;
}, {});

console.log(perCittà);
// {
//   Roma: [{nome: "Mario", ...}, {nome: "Luca", ...}],
//   Milano: [{nome: "Anna", ...}, {nome: "Sara", ...}]
// }
  1. reduce() Avanzato

reduce() Avanzato

// Flatten array (appiattire array annidati)
const arrayAnnidato = [[1, 2], [3, 4], [5, 6]];
const flat = arrayAnnidato.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat);  // [1, 2, 3, 4, 5, 6]

// Pipeline di trasformazioni
const numeri = [1, 2, 3, 4, 5];
const risultato = numeri.reduce((acc, n) => {
    return acc.concat(n * 2);  // Moltiplica per 2
}, []).filter(n => n > 5);     // Poi filtra > 5

console.log(risultato);  // [6, 8, 10]

// Componi funzioni
const funzioni = [
    x => x + 10,
    x => x * 2,
    x => x - 5
];

const composta = funzioni.reduce((acc, fn) => {
    return x => fn(acc(x));
}, x => x);

console.log(composta(5));  // ((5 + 10) * 2) - 5 = 25

// Calcola totale carrello
const carrello = [
    {prodotto: "Laptop", prezzo: 999, quantità: 1},
    {prodotto: "Mouse", prezzo: 25, quantità: 2},
    {prodotto: "Tastiera", prezzo: 75, quantità: 1}
];

const totale = carrello.reduce((acc, item) => {
    return acc + (item.prezzo * item.quantità);
}, 0);

console.log(`Totale: €${totale}`);  // Totale: €1124

⚠️ Valore Iniziale Importante!

Fornire sempre il valore iniziale per evitare comportamenti imprevisti!

const numeri = [1, 2, 3];

// ❌ Senza valore iniziale
const somma1 = numeri.reduce((acc, n) => acc + n);
console.log(somma1);  // 6 (funziona ma...)

// ✅ Con valore iniziale (MEGLIO)
const somma2 = numeri.reduce((acc, n) => acc + n, 0);
console.log(somma2);  // 6

// ❌ Problema senza valore iniziale
const vuoto = [];
// const errore = vuoto.reduce((acc, n) => acc + n);  // TypeError!

// ✅ Con valore iniziale funziona
const sicuro = vuoto.reduce((acc, n) => acc + n, 0);
console.log(sicuro);  // 0

Altri Metodi Utili

Oltre a map, filter e reduce, ci sono altri metodi molto utili per lavorare con array.

  1. find() e findIndex()

Trova il primo elemento che soddisfa la condizione.

find() e findIndex()

const utenti = [
    {id: 1, nome: "Mario", età: 30},
    {id: 2, nome: "Anna", età: 25},
    {id: 3, nome: "Luca", età: 35}
];

// find() - restituisce l'elemento
const utente = utenti.find(u => u.id === 2);
console.log(utente);
// {id: 2, nome: "Anna", età: 25}

// Se non trova, restituisce undefined
const nonEsiste = utenti.find(u => u.id === 99);
console.log(nonEsiste);  // undefined

// findIndex() - restituisce l'indice
const indice = utenti.findIndex(u => u.nome === "Luca");
console.log(indice);  // 2

// Se non trova, restituisce -1
const nonTrovato = utenti.findIndex(u => u.nome === "Paolo");
console.log(nonTrovato);  // -1

// Uso pratico: trova prodotto in carrello
const carrello = [
    {id: 1, nome: "Laptop", quantità: 1},
    {id: 2, nome: "Mouse", quantità: 2}
];

const prodotto = carrello.find(p => p.id === 2);
if (prodotto) {
    prodotto.quantità++;
    console.log(`Quantità aggiornata: ${prodotto.quantità}`);
}
  1. some() e every()

Verifica se almeno uno (some) o tutti (every) soddisfano la condizione.

some() e every()

const numeri = [1, 2, 3, 4, 5];

// some() - almeno uno è vero?
const haPari = numeri.some(n => n % 2 === 0);
console.log(haPari);  // true

const haNegativi = numeri.some(n => n < 0);
console.log(haNegativi);  // false

// every() - tutti sono veri?
const tuttiPositivi = numeri.every(n => n > 0);
console.log(tuttiPositivi);  // true

const tuttiPari = numeri.every(n => n % 2 === 0);
console.log(tuttiPari);  // false

// Uso pratico: validazione form
const campi = [
    {nome: "email", valore: "test@test.com", valido: true},
    {nome: "password", valore: "abc123", valido: true},
    {nome: "telefono", valore: "", valido: false}
];

const tuttiValidi = campi.every(campo => campo.valido);
console.log(`Form valido: ${tuttiValidi}`);  // false

const almeno1Valido = campi.some(campo => campo.valido);
console.log(`Almeno 1 campo valido: ${almeno1Valido}`);  // true

// Controllo età
const persone = [
    {nome: "Mario", età: 25},
    {nome: "Anna", età: 30},
    {nome: "Luca", età: 17}
];

const tuttiMaggiorenni = persone.every(p => p.età >= 18);
console.log(`Tutti maggiorenni: ${tuttiMaggiorenni}`);  // false
  1. sort() e reverse()

Ordinamento e inversione array. ⚠️ Modificano l'array originale!

sort() e reverse()

// sort() - ATTENZIONE: modifica l'originale!
const numeri = [3, 1, 4, 1, 5, 9, 2, 6];

// ❌ Sort di default (ordine lessicografico)
const malOrdinati = [1, 2, 10, 21];
malOrdinati.sort();
console.log(malOrdinati);  // [1, 10, 2, 21] (SBAGLIATO!)

// ✅ Sort numerico corretto
numeri.sort((a, b) => a - b);  // Crescente
console.log(numeri);  // [1, 1, 2, 3, 4, 5, 6, 9]

// Decrescente
numeri.sort((a, b) => b - a);
console.log(numeri);  // [9, 6, 5, 4, 3, 2, 1, 1]

// Sort stringhe
const frutti = ["banana", "mela", "arancia"];
frutti.sort();
console.log(frutti);  // ["arancia", "banana", "mela"]

// Sort oggetti
const persone = [
    {nome: "Mario", età: 30},
    {nome: "Anna", età: 25},
    {nome: "Luca", età: 35}
];

// Per età crescente
persone.sort((a, b) => a.età - b.età);
console.log(persone);
// [{nome: "Anna", età: 25}, ...]

// Per nome alfabetico
persone.sort((a, b) => a.nome.localeCompare(b.nome));

// ✅ Sort IMMUTABILE (non modifica originale)
const numeriOriginali = [3, 1, 4, 1, 5];
const ordinati = [...numeriOriginali].sort((a, b) => a - b);
console.log(numeriOriginali);  // [3, 1, 4, 1, 5] (invariato!)
console.log(ordinati);         // [1, 1, 3, 4, 5]

// reverse() - inverte array
const lettere = ["a", "b", "c"];
lettere.reverse();
console.log(lettere);  // ["c", "b", "a"]

// Immutabile
const originale = [1, 2, 3];
const invertito = [...originale].reverse();
console.log(originale);   // [1, 2, 3]
console.log(invertito);   // [3, 2, 1]

Method Chaining

Puoi concatenare più metodi per creare pipeline di trasformazione eleganti!

Method Chaining

const prodotti = [
    {nome: "Laptop", prezzo: 999, categoria: "elettronica", disponibile: true},
    {nome: "Mouse", prezzo: 25, categoria: "elettronica", disponibile: true},
    {nome: "Sedia", prezzo: 150, categoria: "mobili", disponibile: false},
    {nome: "Monitor", prezzo: 300, categoria: "elettronica", disponibile: true},
    {nome: "Scrivania", prezzo: 200, categoria: "mobili", disponibile: true}
];

// ✨ Pipeline: filtra → mappa → ordina → riduci
const totaleElettronica = prodotti
    .filter(p => p.categoria === "elettronica")  // Solo elettronica
    .filter(p => p.disponibile)                  // Solo disponibili
    .map(p => p.prezzo)                          // Estrai prezzi
    .reduce((acc, prezzo) => acc + prezzo, 0);   // Somma

console.log(`Totale elettronica: €${totaleElettronica}`);  // €1324

// Nomi prodotti disponibili in maiuscolo, ordinati
const nomiOrdinati = prodotti
    .filter(p => p.disponibile)
    .map(p => p.nome.toUpperCase())
    .sort();

console.log(nomiOrdinati);
// ["LAPTOP", "MONITOR", "MOUSE", "SCRIVANIA"]

// Top 3 prodotti più costosi (nomi)
const top3 = prodotti
    .sort((a, b) => b.prezzo - a.prezzo)  // Ordina per prezzo desc
    .slice(0, 3)                          // Primi 3
    .map(p => p.nome);                    // Solo nomi

console.log(top3);  // ["Laptop", "Monitor", "Scrivania"]

// Media prezzi categoria elettronica
const mediaElettronica = prodotti
    .filter(p => p.categoria === "elettronica")
    .map(p => p.prezzo)
    .reduce((acc, p, i, arr) => {
        acc += p;
        return i === arr.length - 1 ? acc / arr.length : acc;
    }, 0);

console.log(`Media elettronica: €${mediaElettronica.toFixed(2)}`);

// Conta prodotti per categoria
const perCategoria = prodotti
    .reduce((acc, p) => {
        acc[p.categoria] = (acc[p.categoria] || 0) + 1;
        return acc;
    }, {});

console.log(perCategoria);  // {elettronica: 3, mobili: 2}

💡 Best Practices Chaining

  • ✅ Un metodo per riga per leggibilità
  • ✅ Filtra PRIMA di mappare (meno iterazioni)
  • ✅ Ordina DOPO aver ridotto i dati
  • ⚠️ Attenzione a performance con array grandi
  • ⚠️ Ogni metodo crea nuovo array (memoria)

Confronto Metodi

Tabella Decisionale

Voglio... Metodo Restituisce Esempio
Trasformare ogni elemento map() Nuovo array (stessa lunghezza) Doppio di ogni numero
Selezionare elementi filter() Nuovo array (≤ originale) Solo numeri pari
Aggregare a valore singolo reduce() Singolo valore (qualsiasi tipo) Somma, media, oggetto
Trovare UN elemento find() Elemento o undefined Utente con id=5
Trovare posizione findIndex() Indice o -1 Indice utente "Mario"
Verificare se ALMENO UNO some() Boolean C'è almeno 1 pari?
Verificare se TUTTI every() Boolean Tutti maggiorenni?

Esempio Completo: Analytics Dashboard

Analytics Dashboard Completo

// Dataset vendite e-commerce
const vendite = [
    {id: 1, prodotto: "Laptop", categoria: "elettronica", prezzo: 999, quantità: 2, data: "2025-01-15"},
    {id: 2, prodotto: "Mouse", categoria: "elettronica", prezzo: 25, quantità: 5, data: "2025-01-15"},
    {id: 3, prodotto: "Sedia", categoria: "mobili", prezzo: 150, quantità: 1, data: "2025-01-16"},
    {id: 4, prodotto: "Monitor", categoria: "elettronica", prezzo: 300, quantità: 3, data: "2025-01-16"},
    {id: 5, prodotto: "Scrivania", categoria: "mobili", prezzo: 200, quantità: 2, data: "2025-01-17"},
    {id: 6, prodotto: "Tastiera", categoria: "elettronica", prezzo: 75, quantità: 4, data: "2025-01-17"},
    {id: 7, prodotto: "Lampada", categoria: "mobili", prezzo: 45, quantità: 3, data: "2025-01-18"}
];

// === ANALYTICS FUNCTIONS ===

// 1. Ricavi totali
const ricaviTotali = vendite
    .map(v => v.prezzo * v.quantità)
    .reduce((acc, ricavo) => acc + ricavo, 0);

console.log(`💰 Ricavi totali: €${ricaviTotali.toLocaleString()}`);

// 2. Ricavi per categoria
const ricaviPerCategoria = vendite
    .reduce((acc, v) => {
        const ricavo = v.prezzo * v.quantità;
        acc[v.categoria] = (acc[v.categoria] || 0) + ricavo;
        return acc;
    }, {});

console.log("\n📊 Ricavi per categoria:");
Object.entries(ricaviPerCategoria).forEach(([cat, ricavo]) => {
    console.log(`  ${cat}: €${ricavo.toLocaleString()}`);
});

// 3. Prodotto più venduto (per quantità)
const prodottoPiùVenduto = vendite
    .reduce((max, v) => v.quantità > max.quantità ? v : max);

console.log(`\n🏆 Prodotto più venduto: ${prodottoPiùVenduto.prodotto} (${prodottoPiùVenduto.quantità} unità)`);

// 4. Top 3 prodotti per ricavo
const top3Ricavi = vendite
    .map(v => ({
        prodotto: v.prodotto,
        ricavo: v.prezzo * v.quantità
    }))
    .sort((a, b) => b.ricavo - a.ricavo)
    .slice(0, 3);

console.log("\n🥇 Top 3 prodotti per ricavo:");
top3Ricavi.forEach((item, i) => {
    console.log(`  ${i + 1}. ${item.prodotto}: €${item.ricavo.toLocaleString()}`);
});

// 5. Prezzo medio prodotti
const prezzoMedio = vendite
    .map(v => v.prezzo)
    .reduce((acc, p, i, arr) => {
        acc += p;
        return i === arr.length - 1 ? acc / arr.length : acc;
    }, 0);

console.log(`\n💵 Prezzo medio: €${prezzoMedio.toFixed(2)}`);

// 6. Vendite per giorno
const venditePerGiorno = vendite
    .reduce((acc, v) => {
        if (!acc[v.data]) {
            acc[v.data] = {
                transazioni: 0,
                ricavo: 0,
                prodotti: []
            };
        }
        acc[v.data].transazioni++;
        acc[v.data].ricavo += v.prezzo * v.quantità;
        acc[v.data].prodotti.push(v.prodotto);
        return acc;
    }, {});

console.log("\n📅 Vendite per giorno:");
Object.entries(venditePerGiorno).forEach(([data, stats]) => {
    console.log(`  ${data}:`);
    console.log(`    Transazioni: ${stats.transazioni}`);
    console.log(`    Ricavo: €${stats.ricavo.toLocaleString()}`);
    console.log(`    Prodotti: ${stats.prodotti.join(", ")}`);
});

// 7. Categoria dominante
const categoriaDominante = Object.entries(ricaviPerCategoria)
    .reduce((max, [cat, ricavo]) => {
        return ricavo > max.ricavo ? {categoria: cat, ricavo} : max;
    }, {categoria: "", ricavo: 0});

console.log(`\n👑 Categoria dominante: ${categoriaDominante.categoria} (€${categoriaDominante.ricavo.toLocaleString()})`);

// 8. Prodotti sopra prezzo medio
const sopraPrezzoMedio = vendite
    .filter(v => v.prezzo > prezzoMedio)
    .map(v => v.prodotto);

console.log(`\n💎 Prodotti sopra prezzo medio (€${prezzoMedio.toFixed(2)}):`);
console.log(`  ${sopraPrezzoMedio.join(", ")}`);

// 9. Statistiche complete
const stats = {
    totaleTransazioni: vendite.length,
    ricaviTotali: ricaviTotali,
    ricavoMedio: ricaviTotali / vendite.length,
    prezzoMedio: prezzoMedio,
    quantitàTotale: vendite.reduce((acc, v) => acc + v.quantità, 0),
    categorie: Object.keys(ricaviPerCategoria).length,
    giorniAttivi: Object.keys(venditePerGiorno).length
};

console.log("\n📈 STATISTICHE COMPLETE:");
console.log(`  Transazioni: ${stats.totaleTransazioni}`);
console.log(`  Ricavi totali: €${stats.ricaviTotali.toLocaleString()}`);
console.log(`  Ricavo medio/transazione: €${stats.ricavoMedio.toFixed(2)}`);
console.log(`  Prezzo medio prodotti: €${stats.prezzoMedio.toFixed(2)}`);
console.log(`  Unità vendute: ${stats.quantitàTotale}`);
console.log(`  Categorie: ${stats.categorie}`);
console.log(`  Giorni attivi: ${stats.giorniAttivi}`);

// Output:
// 💰 Ricavi totali: €5.348
//
// 📊 Ricavi per categoria:
//   elettronica: €3.423
//   mobili: €1.925
//
// 🏆 Prodotto più venduto: Mouse (5 unità)
//
// 🥇 Top 3 prodotti per ricavo:
//   1. Laptop: €1.998
//   2. Monitor: €900
//   3. Scrivania: €400
//
// 💵 Prezzo medio: €256.29
//
// 📅 Vendite per giorno:
//   2025-01-15:
//     Transazioni: 2
//     Ricavo: €2.123
//     Prodotti: Laptop, Mouse
//   ... (continua)
//
// 👑 Categoria dominante: elettronica (€3.423)
//
// 💎 Prodotti sopra prezzo medio (€256.29):
//   Laptop, Monitor
//
// 📈 STATISTICHE COMPLETE:
//   Transazioni: 7
//   Ricavi totali: €5.348
//   Ricavo medio/transazione: €764.00
//   Prezzo medio prodotti: €256.29
//   Unità vendute: 20
//   Categorie: 2
//   Giorni attivi: 4

💡 Tecniche Usate

  • map() - Calcola ricavi, estrae proprietà
  • filter() - Filtra per condizioni
  • reduce() - Somme, raggruppamenti, oggetti complessi
  • sort() - Ordinamenti per ranking
  • slice() - Top N elementi
  • Method chaining - Pipeline di trasformazioni
  • Object destructuring - Estrazione valori

Esercizi Pratici

Esercizio 1: Analisi Studenti

Obiettivo: Analizza array di studenti con voti

  • Calcola media voti di ogni studente
  • Trova studenti promossi (media ≥ 6)
  • Calcola media classe
  • Trova studente migliore
Mostra la soluzione

Soluzione Esercizio 1

const studenti = [
    {nome: "Mario", voti: [7, 8, 6, 9, 7]},
    {nome: "Anna", voti: [9, 9, 8, 10, 9]},
    {nome: "Luca", voti: [5, 6, 5, 7, 6]},
    {nome: "Sara", voti: [8, 7, 9, 8, 8]},
    {nome: "Paolo", voti: [4, 5, 5, 6, 5]}
];

// 1. Calcola media ogni studente
const conMedia = studenti.map(studente => ({
    ...studente,
    media: studente.voti.reduce((acc, v) => acc + v, 0) / studente.voti.length
}));

console.log("=== MEDIE STUDENTI ===");
conMedia.forEach(s => {
    console.log(`${s.nome}: ${s.media.toFixed(2)}`);
});

// 2. Studenti promossi
const promossi = conMedia.filter(s => s.media >= 6);

console.log("\n✅ PROMOSSI:");
promossi.forEach(s => {
    console.log(`  ${s.nome} (media: ${s.media.toFixed(2)})`);
});

// 3. Media classe
const mediaClasse = conMedia
    .map(s => s.media)
    .reduce((acc, m) => acc + m, 0) / conMedia.length;

console.log(`\n📊 Media classe: ${mediaClasse.toFixed(2)}`);

// 4. Studente migliore
const migliore = conMedia.reduce((max, s) =>
    s.media > max.media ? s : max
);

console.log(`\n🏆 Studente migliore: ${migliore.nome} (${migliore.media.toFixed(2)})`);

// 5. Statistiche complete
const stats = {
    totaleStudenti: studenti.length,
    promossi: promossi.length,
    bocciati: studenti.length - promossi.length,
    mediaClasse: mediaClasse,
    votoMinimo: Math.min(...conMedia.map(s => s.media)),
    votoMassimo: Math.max(...conMedia.map(s => s.media))
};

console.log("\n📈 STATISTICHE:");
console.log(`  Studenti: ${stats.totaleStudenti}`);
console.log(`  Promossi: ${stats.promossi}`);
console.log(`  Bocciati: ${stats.bocciati}`);
console.log(`  Media classe: ${stats.mediaClasse.toFixed(2)}`);
console.log(`  Voto minimo: ${stats.votoMinimo.toFixed(2)}`);
console.log(`  Voto massimo: ${stats.votoMassimo.toFixed(2)}`);

// Output:
// === MEDIE STUDENTI ===
// Mario: 7.40
// Anna: 9.00
// Luca: 5.80
// Sara: 8.00
// Paolo: 5.00
//
// ✅ PROMOSSI:
//   Mario (media: 7.40)
//   Anna (media: 9.00)
//   Sara (media: 8.00)
//
// 📊 Media classe: 7.04
//
// 🏆 Studente migliore: Anna (9.00)
//
// 📈 STATISTICHE:
//   Studenti: 5
//   Promossi: 3
//   Bocciati: 2
//   Media classe: 7.04
//   Voto minimo: 5.00
//   Voto massimo: 9.00

Esercizio 2: Carrello E-commerce

Obiettivo: Gestisci carrello con sconti e totali

  • Calcola subtotale per prodotto
  • Applica sconto categoria "elettronica" (10%)
  • Calcola totale carrello
  • Aggiungi spedizione gratuita se > €100
Mostra la soluzione

Soluzione Esercizio 2

const carrello = [
    {prodotto: "Laptop", prezzo: 999, quantità: 1, categoria: "elettronica"},
    {prodotto: "Mouse", prezzo: 25, quantità: 2, categoria: "elettronica"},
    {prodotto: "Sedia", prezzo: 150, quantità: 1, categoria: "mobili"},
    {prodotto: "Monitor", prezzo: 300, quantità: 1, categoria: "elettronica"}
];

// 1. Calcola subtotale e applica sconto
const conSubtotale = carrello.map(item => {
    const subtotale = item.prezzo * item.quantità;
    const sconto = item.categoria === "elettronica" ? 0.10 : 0;
    const importoSconto = subtotale * sconto;
    const totale = subtotale - importoSconto;

    return {
        ...item,
        subtotale,
        sconto: sconto * 100,
        importoSconto,
        totale
    };
});

console.log("=== CARRELLO ===");
conSubtotale.forEach(item => {
    console.log(`${item.prodotto} x${item.quantità}`);
    console.log(`  Subtotale: €${item.subtotale.toFixed(2)}`);
    if (item.sconto > 0) {
        console.log(`  Sconto ${item.sconto}%: -€${item.importoSconto.toFixed(2)}`);
    }
    console.log(`  Totale: €${item.totale.toFixed(2)}\n`);
});

// 2. Calcola totale carrello
const totaleCarrello = conSubtotale
    .map(item => item.totale)
    .reduce((acc, totale) => acc + totale, 0);

console.log(`Subtotale prodotti: €${totaleCarrello.toFixed(2)}`);

// 3. Spedizione
const SOGLIA_SPEDIZIONE = 100;
const COSTO_SPEDIZIONE = 10;

const spedizione = totaleCarrello >= SOGLIA_SPEDIZIONE ? 0 : COSTO_SPEDIZIONE;

if (spedizione === 0) {
    console.log("Spedizione: GRATIS ✅");
} else {
    console.log(`Spedizione: €${spedizione.toFixed(2)}`);
}

// 4. Totale finale
const totalefinale = totaleCarrello + spedizione;
console.log(`\n💰 TOTALE: €${totaleFinale.toFixed(2)}`);

// 5. Risparmio totale
const totaleRisparmiato = conSubtotale
    .map(item => item.importoSconto)
    .reduce((acc, sconto) => acc + sconto, 0);

console.log(`💚 Hai risparmiato: €${totaleRisparmiato.toFixed(2)}`);

// 6. Riepilogo
const riepilogo = {
    articoli: carrello.reduce((acc, item) => acc + item.quantità, 0),
    subtotale: totaleCarrello,
    sconti: totaleRisparmiato,
    spedizione: spedizione,
    totale: totaleFinale
};

console.log("\n📋 RIEPILOGO:");
console.log(`  Articoli: ${riepilogo.articoli}`);
console.log(`  Subtotale: €${riepilogo.subtotale.toFixed(2)}`);
console.log(`  Sconti: -€${riepilogo.sconti.toFixed(2)}`);
console.log(`  Spedizione: €${riepilogo.spedizione.toFixed(2)}`);
console.log(`  ═══════════════════`);
console.log(`  TOTALE: €${riepilogo.totale.toFixed(2)}`);

// Output:
// === CARRELLO ===
// Laptop x1
//   Subtotale: €999.00
//   Sconto 10%: -€99.90
//   Totale: €899.10
//
// Mouse x2
//   Subtotale: €50.00
//   Sconto 10%: -€5.00
//   Totale: €45.00
// ... (continua)
//
// Subtotale prodotti: €1,319.10
// Spedizione: GRATIS ✅
//
// 💰 TOTALE: €1,319.10
// 💚 Hai risparmiato: €137.40
//
// 📋 RIEPILOGO:
//   Articoli: 5
//   Subtotale: €1,319.10
//   Sconti: -€137.40
//   Spedizione: €0.00
//   ═══════════════════
//   TOTALE: €1,319.10

Esercizio 3: Word Frequency

Obiettivo: Analizza testo e conta frequenza parole

  • Split testo in parole
  • Rimuovi punteggiatura e converti minuscolo
  • Conta occorrenze ogni parola
  • Ordina per frequenza e mostra top 5
Mostra la soluzione

Soluzione Esercizio 3

const testo = `
JavaScript è il linguaggio di programmazione più popolare per il web.
JavaScript permette di creare applicazioni web interattive e dinamiche.
Molti sviluppatori usano JavaScript ogni giorno per sviluppare siti web moderni.
JavaScript è versatile e potente, e JavaScript continua a evolversi.
`;

// 1. Pulisci e split
const parole = testo
    .toLowerCase()                        // Minuscolo
    .replace(/[.,\n]/g, '')               // Rimuovi punteggiatura
    .split(' ')                           // Split in parole
    .filter(p => p.length > 0);           // Rimuovi vuoti

console.log(`📝 Totale parole: ${parole.length}`);

// 2. Conta frequenze
const frequenze = parole.reduce((acc, parola) => {
    acc[parola] = (acc[parola] || 0) + 1;
    return acc;
}, {});

console.log("\n📊 Frequenze:");
console.log(frequenze);

// 3. Converti in array ordinato
const ordinato = Object.entries(frequenze)
    .map(([parola, count]) => ({parola, count}))
    .sort((a, b) => b.count - a.count);

// 4. Top 5
console.log("\n🏆 TOP 5 PAROLE:");
ordinato.slice(0, 5).forEach((item, i) => {
    console.log(`${i + 1}. "${item.parola}" - ${item.count} volte`);
});

// 5. Statistiche
const stats = {
    totaleParole: parole.length,
    paroleUniche: Object.keys(frequenze).length,
    parolaPiùComune: ordinato[0],
    lunghezzaMedia: parole.reduce((acc, p) => acc + p.length, 0) / parole.length
};

console.log("\n📈 STATISTICHE:");
console.log(`  Totale parole: ${stats.totaleParole}`);
console.log(`  Parole uniche: ${stats.paroleUniche}`);
console.log(`  Parola più comune: "${stats.parolaPiùComune.parola}" (${stats.parolaPiùComune.count}x)`);
console.log(`  Lunghezza media: ${stats.lunghezzaMedia.toFixed(2)} caratteri`);

// 6. Parole lunghe (> 8 caratteri)
const paroleLunghe = parole
    .filter(p => p.length > 8)
    .filter((p, i, arr) => arr.indexOf(p) === i);  // Unici

console.log(`\n📏 Parole > 8 caratteri:`);
console.log(`  ${paroleLunghe.join(", ")}`);

// Output:
// 📝 Totale parole: 38
//
// 📊 Frequenze:
// {
//   javascript: 5,
//   è: 3,
//   il: 2,
//   ... (continua)
// }
//
// 🏆 TOP 5 PAROLE:
// 1. "javascript" - 5 volte
// 2. "è" - 3 volte
// 3. "web" - 3 volte
// 4. "il" - 2 volte
// 5. "per" - 2 volte
//
// 📈 STATISTICHE:
//   Totale parole: 38
//   Parole uniche: 27
//   Parola più comune: "javascript" (5x)
//   Lunghezza media: 6.84 caratteri
//
// 📏 Parole > 8 caratteri:
//   linguaggio, programmazione, applicazioni, interattive, ...

🎯 Riepilogo Lezione

map()

  • Trasforma elementi
  • Stessa lunghezza
  • Return obbligatorio

filter()

  • Seleziona elementi
  • Return true/false
  • Lunghezza variabile

reduce()

  • Aggrega valori
  • Valore singolo
  • Valore iniziale!

Chaining

  • Concatena metodi
  • Pipeline dati
  • Filtra prima!