Manipolazione dei dati
Array
Un array è un tipo di dato che raccoglie in modo ordinato una lista di valori. Si differenzia dal tradizionale array C per due ragioni:
- gli elementi possono essere di tipo diverso;
- non ha una dimensione predefinita.
Analogamente al C l’accesso ai dati è tramite un indice numerico.
let lista = [1,2,3,4,5,6];
let nomi = ["Mario", "Antonio", "Luigi"];
let matrix = [[1,2,3],[4,5,6],[7,8,9]];
let mixed = [1, False, "Sandra", 36, "Antonio", [4,5], null ];
let complicated = [1, 2, [4,5]];Gli array possono contenere qualsiasi tipo di dato, quindi altri array come visto sopra, ma anche dizionari (vedi sotto) e funzioni:
const lista = ["Mario", "Rossi", () => console.log("ciao")];
lista[2](); // esegue la funzione in quella posizioneOperazioni principali:
console.log(lista[6]); // gli indici partono da 0
nomi[3] = "Carlo";Gli array sono tipi riferimento, l’equivalente dei tipi puntatore C.
Pertanto la variabile non contiene direttamente l’array ma è un nome (più precisamente un simbolo) associato all’area di memoria dove è realmente memorizzato l’array.
Pertanto non è possibile eseguire una copia di un array con una semplice assegnazione, perché la nuova variabile sarebbe un alias, e non un duplicato dell’originale. Questo tipo di copia viene chiamato “shallow copy” (copia superficiale).
const a = [1,2,3];
const b = a;
b[0] = 3;
console.log(a[0]); // stampa 3, perché a coincide con bPer eseguire una “deep copy” (copia profonda) bisogna copiare ogni singolo elemento, nel nuovo array.
Cicli su array
Per eseguire un ciclo abbiamo due possibilità:
- for..of
Esegue un ciclo sull’array:
const array = ["a", "b", "c"];
for (const element of array) {
console.log(element);
}- forEach()
Esegue un ciclo sull’array tramite una funzione:
array.forEach((element) => {
console.log(element);
}Estrazione ed inserimento elementi
Le operazioni principali sono:
- push(element): inserisce un elemento in coda
- pop(): estrae ed elimina un elemento in coda
- shift(): estrae ed elimina un elemento in testa
- unshift(element): inserisce un elemento in testa
- splice(start, count): estrae un array di elementi che parte da start (e li toglie dall’array)
- slice(start, end): estrae un array di elementi dall’indice start all’indice end escluso (ma non li toglie dall’array)
mixed.push(True); //inserisce nuovo valore
const last = mixed.pop(); //rimuove ultimo valore
const first = list.shift(); // estrae il primo valore e lo rimuove
list.unshift(12); // inserisce 12 in testa
const removed = lista.splice(3,5); // estrae e rimuove 5 elementi dall'indice 3
const extracted = lista.slice(3,5); // estrae gli elementi da 3 a 5 esclusoMap
Map crea un nuovo array eseguendo una funzione di trasformazione per ogni elemento dell’array.
const list = [1,2,3];
const newList = list.map(element => {
return element*2;
)};
// newList conterrà [2,4,6]Map è un costrutto di programmazione funzionale, in quanto non opera, come nella programmazione imperativa, che lavora ad un maggior livello di dettaglio, cioè partendo da un indice ed una lunghezza dell’array. Map considera l’array come un insieme di valori ordinati e si occupa solo di applicare una funzione sul singolo elemento dell’array, tralasciando altri dettagli, come trovare l’indice e controllare la lunghezza.
Filter
La funzione array filter() consente di estrarre un sottoarray di elementi. La funzione riceve come argomento una funzione che definisce la regola che verifica se il singolo elemento deve essere selezionato oppure no.
const values = [1,2,3,4,5,6,7,8,9,10];
const even = values.filter((value) => {
return value % 2 === 0;
);
// forma compatta
const even2 = values.filter(v => v % 2 === 0);La funzione che definisce la regola viene chiamata predicato, ed è una funzione che restituisce un valore booleano. Anche qui, come in map, abbiamo un costrutto di tipo funzionale: spetta al linguaggio implementare internamente il ciclo su tutti gli elementi.
Find
Find è molto simile a filter ma restituisce solo il primo elemento che soddisfa il predicato.
const values = [1,2,3,4,5,6,7,8,9,10];
const even = values.find((value) => {
return value % 2 === 0;
); // 2
// forma compatta
const even2 = values.find(v => v % 2 === 0);Sort
E’ possibile ordinare un array usando la funzione sort():
const list = [1,3,2];
list.sort(); // -> [1,2,3]E’ possibile anche passare alla funzione sort una funzione predicato che definisce la relazione tra due singoli elementi dell’array ovvero il criterio di ordinamento.
const list = [1,3,2];
list.sort((a,b) => {
return a < b;
}); // -> [3,2,1]
//forma compatta:
list.sort(a,b => a<b);La funzione di comparazione non deve restituire necessariamente un valore booleano, può restituire anche un valore numerico: se minore di 0, equivale a false e i due valori sono invertiti, se maggiore o uguale a 0 non sono invertiti.
const list = [1,3,2];
//forma compatta:
list.sort(a,b => a-b);Reduce
La reduce è una funzione che ha come scopo quello di calcolare un unico valore a partire da tutti i valori di un array. E’ utile per esempio per calcolare la somma degli elementi di un array, o per accumulare insieme tutti gli elementi in un valore unico.
La reduce è una funzione che riceve due argomenti:
- una funzione detta di accumulazione, che a sua volta riceve due argomenti:
- una variabile accumulatore, ovvero la variabile che conterrà l’unico valore finale
- il valore corrente
- il valore iniziale dell’accumulatore
La funzione di accumulazione viene eseguita su ogni elemento dell’array (come map o filter) e modifica il valore dell’accumulatore mano a mano che viene eseguita su ogni elemento.
Qui vediamo un esempio con la somma di tutti gli elementi di un array:
const values = [1,2,3,4,5,6,7,8,9,10];
const sum = values.reduce((acc, value) => {
acc += value;
}, 0);
//In forma compatta:
const sum2 = values.reduce((a,v) => a+=v, 0);Spread (“…”)
Usando l’operatore spread è possibile elencare gli elementi senza dover iterare su di essi. Ad esempio:
let fruits = ["mele", "pere", "banane"];
let vegetables = ["cipolle", "cicoria", "patate"];
fruits = ["fragole", "lamponi", ...fruits]; // aggiunge degli elementi in testa
fruits = [...fruits, "pesche"]; // aggiunge in coda
const food = [...a1, ...2]; // è possibile unire più array con spreadPer duplicare un array si può usare l’operatore spread:
const a = [36,21,18,9,71,33];
const b = [...a];Stringhe e array
Le stringhe possono essere trasformate in array di sottostringhe e viceversa.
Con join è possibile unire tutti gli elementi di un array in una stringa, con un separatore.
const lotto = [36,21,18,9,71,33];
const lottoString = lotto.join(",");
// "36,21,18,9,71,33"L’operazione di split consente di trasformare una stringa in un array suddividendola per un carattere separatore.
const string = "Mi piace molto Javascript"
const list = string.split(" ");
// ["Mi", "piace", "molto", "Javascript"]Dizionari
Un dizionario è un oggetto che raccoglie un insieme di coppie nome-valore, dove nome è sempre una stringa, e valore può essere qualsiasi oggetto Javascript.
let dict = {
nome: "Mario"
cognome: "Rossi"
};Si può accedere ad un dizionario in due modi:
console.log(dict["nome"]);
console.log(dict.nome);Occorre fare attrenzione su questo. Questi due tipi di accesso sono indicazione di due modi diversi di intendere l’accesso al dizionario.
Se si usa dict[“nome”] è possibile in realtà usare come chiave sia una stringa literal, sia una variabile:
const key = "nome";
const name = dict[key];Poter usare una variabile come chiave permette di individuarla a runtime, in altre parole la scelta della chiave da usare non è scelta staticamente a compile time.
Se si usa dict.nome invece il nome della chiave è conosciuto solo a compile time, garantendo in generale minore flessibilità di utilizzo.
E’ sempre possibile aggiungere proprietà ad un dizionario esistente:
let dict = {
nome: "Mario"
cognome: "Rossi"
};
dict.età = 26;
dict["città"] = "Roma";Un dizionario è un tipo riferimento. Quindi per duplicare un dizionario in un altro è sempre necessaria la deep copy. Per farlo è possibile usare l’operatore spread:
let dict = {
nome: "Mario"
cognome: "Rossi"
};
const b = {...a};Spread può essere usato, come negli array, per aggiungere chiavi:
let dict = {
nome: "Mario"
cognome: "Rossi"
};
dict = {
età: 26,
città: "Milano",
...dict
}Il dizionario è un oggetto standard Javascript. Esso come detto sopra può contenere come proprietà come dizionari, array, tipi base e funzioni. Le proprietà di tipo funzione sono dette metodi, usando la terminologia della programmazione ad oggetti.
let dict = {
nome: "Mario"
cognome: "Rossi",
saluta: () => console.log("ciao")
};Cicli su dizionari
E’ possibile iterare sulle chiavi del dizionario con il costrutto for in:
for (const key in dict) {
console.log(dict[key])
}E’ possibile estrarre l’array delle chiavi o dei valori dell’array
const keys = Object.keys(dict);
const values = Object.values(dict);E quindi utilizzare poi le operazioni sui cicli viste sopra.
Array di dizionari
Gli array di dizionari sono una struttura dati che combina l’array ed il dizionario, ed è una delle strutture più diffuse in Javascript.
const list = [
{
"nome": "Mario"
"cognome": "Rossi"
},
{
"nome": "Paola"
"cognome": "Bianchi"
},
]Si comportano come array ma consentono di gestire, meglio ad esempio degli array bidimensionali, strutture dati di tipo tabellare. Ad esempio è possibile eseguire un sort:
const students = [
{
name: "Luca Rossi",
vote: 8.5
},
{
name: "Giulia Bianchi",
vote: 9.0
},
{
name: "Marco Verdi",
vote: 7.5
},
];
students.sort((a,b) => a.vote - b.vote);
/*
Risultato:
[{
name: "Giulia Bianchi",
vote: 9
}, {
name: "Luca Rossi",
vote: 8.5
}, {
name: "Marco Verdi",
vote: 7.5
}]
*/CSV
Un CSV (Comma Separated Values) è un file di testo semplice formattato per contenere dati in forma tabellare, dove ogni riga contiene valori separati da virgole (in alcuni casi si usa il punto e virgola ; o un altro carattere separatore). E’ presente opzionalmente una prima riga contenente (sempre con valori separati da virgola) una intestazione. I dati sono normalmente esportazioni da comuni applicazioni di foglio di calcolo (come Excel) o database.
Il CSV è uno dei tre formati principali di interscambio dati su Internet (insieme a JSON e XML).
Qui un esempio di articoli a magazzino di un supermercato:
Nome, Tipo, Prezzo, Quantità
pane, alimenti, 2, 10
latte, alimenti, 1, 50
acqua, alimenti, 0.25, 50
detersivo, casalinghi, 3, 20
microonde, elettrodomestici, 100, 3
carta igienica, casalinghi, 4, 100E’ possibile rappresentare un CSV in due modi:
Da CSV ad array bidimensionale
Si crea un array dove gni elemento è una lista contenente i valori di una riga. E’ poi presente una prima riga con le intestazioni (o in alternativa in una variabile indipendente che contiene la sola intestazione). Possiamo importare un csv con una funzione di importazione che usa split e map. Qui il codice:
csv = `Nome, Tipo, Prezzo, Quantità
pane, alimenti, 2, 10
latte, alimenti, 1, 50
acqua, alimenti, 0.25, 50
detersivo, casalinghi, 3, 20
microonde, elettrodomestici, 100, 3
carta igienica, casalinghi, 4, 100`;
csv = csv.replaceAll(' ', ''); // eliminiamo gli spazi
const rows = csv.split('\n'); // dividiamo il csv in righe
const header = rows.shift().split(','); // estraiamo un array con l'header
const data = rows.map((row) => row.split(',')); // estraiamo la tabella
/*
header -> ["Nome", "Tipo", "Prezzo", "Quantità"]
data ->
[
["pane", "alimenti", "2", "10"],
["latte", "alimenti", "1", "50"],
...
]
*/Come si può notare, shift, split e map rendono l’operazione di estrazione una operazione molto semplice.
Da CSV ad array di dizionari
Si crea un array dove ogni elemento è un dizionario dove le chiavi sono quelle dell’header e i valori quelli contenuti in ogni riga.
Se le chiavi non sono conosciute a compile time (ad esempio perché si tratta di un dato scaricato da Internet), le chiavi vanno prese dall’header, come in questo esempio:
let csv = `...`; //
csv = csv.replaceAll(' ', ''); // eliminiamo gli spazi
const rows = csv.split('\n'); // estraiamo le righe
const header = rows.shift().split(','); // estraiamo l'header
const data = rows.map((row) => {
const cols = row.split(','); // estraiamo le colonne della riga
const dict = {}; // creiamo un nuovo dizionario
row.forEach((col, index) => { // per ogni colonna
dict[header[index]] = col; // associamo l'header[index] al valore col
})
return dict;
})
/*
header -> ["Nome", "Tipo", "Prezzo", "Quantità"]
data ->
{
{
"Nome": pane",
"Tipo": "alimenti",
"Prezzo": "2",
"Quantità": "10"],
},
{
"Nome": latte",
"Tipo": "alimenti",
"Prezzo": "1",
"Quantità": "50"],
},
...
}
*/Se invece i nomi delle colonne (e i tipi di dato) sono già conosciuti a compile time, allora è possibile costruire il dizionario in modo esplicito:
csv = `nome, categoria, prezzo, quantita
pane, alimenti, 2, 108
latte, alimenti, 1, 89
detersivo, casalinghi, 2, 53`;
const rows = csv.trim().split('\n');
const header = rows.shift().split(',');
const data = rows.map((row) => {
const cols = row.split(',');
const dict = {
nome: cols[0],
categoria: cols[1],
prezzo: parseInt(cols[2]),
quantita: parseInt(cols[3])
}
return dict;
});Creare un CSV
E’ possibile svolgere anche l’operazione opposta ovvero creare un CSV da un array (caso 1):
const csv = header.join(",") + "\n";
csv += data.map((row) => row.join(",")).join("\n");Oppure da un dizionario (caso 2):
const rows = data.map((dataRow) => Object.values(dataRow).join(','); // viene creato
// un array di righe stringa
let csv = header.join(",") + "\n"; // si inizializza il csv con l'header
csv += rows.join("\n"); // si aggiungono le righe separandole con il ritorno a capoJSON
Il formato JSON (Javascript Object Notation) è un formato di testo usato da Javascript per memorizzare in una unica stringa i dati provenienti da array e dizionari. Questo testo può essere quindi utilizzato per essere salvato su file, o lo storage del browsers, ma soprattutto per inviarlo e riceverlo da remoto su Internet.
JSON proprio grazie alla grande diffusione di Javascript è il principale formato di scambio dati su Internet, anche nelle applicazioni scritte con altri linguaggi. [1]
Viene usato in molti ambiti: trasferire dati su Internet (come vedremo), memorizzare informazioni strutturate su file di testo, memorizzare strutture dati su database, file di configurazione, ecc.
JSON permette di salvare sia array che dizionari di qualsiasi dimensione, a patto che questi contengano al loro interno solo valori array, dizionario o tipo base. In altre parole non è possibile trasformare in JSON strutture dati che contengono funzioni.
Javascript permette di convertire un array o un dizionario in JSON con la funzione JSON.stringify:
const jsonString = JSON.stringify(variabile);Viceversa possiamo trasformare la stringa JSON in un oggetto con l’istruzione JSON.parse:
const obj = JSON.parse(stringaJSON);[1] Altri formati molto noti sono CSV (testo in colonne separate da virgola, tipico di export da programmi come Excel) e XML (formato dati a markup, di cui HTML è una derivazione).
[2] Sono esclusi gli oggetti o gli array che contengono funzioni.
