Link Immagine: https://bit.ly/2WLvWhV

Array in JS: un mondo di possibilità!

Francesco Sciuti
Devmy
Published in
11 min readJun 17, 2019

--

In JavaScript, un Array è una singola variabile che viene utilizzata per memorizzare diversi elementi. Sono spesso usate quando vogliamo memorizzare una lista di elementi e accedervi con una singola variabile. È un modo più conveniente per memorizzare e strutturare le informazioni rispetto alla definizione di molte variabili con nomi leggermente diversi.

Per essere precisi, come detto dalla pagina MDN:

Gli Array sono oggetti di tipo lista il cui prototipo ha metodi per performare operazioni trasversali e di mutazione. Nè la lunghezza di un array JavaScript o i tipi dei suoi elementi sono fissati.

Gli array JavaScript sono zero-indicizzati: il primo elemento di un array è all’inidice0, e l'ultimo elemento è all'indice uguale al valore della proprietà dell'array length meno 1.

Ma in fondo tutte queste cose le sappiamo già!

Quello che forse non sappiamo è quanto potenti possano essere gli Array in JavaScript e quanto possano ridurre la quantità di codice da redigere (e quindi che potenzialmente possa contenere bugs che, si sa, sono sempre dietro l’angolo).
In particolar modo, dall’arrivo di ES6 e successivi il mondo degli Array si è arricchito di tantissime possibilità e molte di queste si riflettono sul loro utilizzo in maniera piuttosto importante.

Questo articolo desidera essere una sorta di comodo decalogo (o Cookbook?) di qualche comune caso d’uso che possiamo incontrare e quindi affrontare in maniera intelligente!

I metodi e le proprietà più comuni

Facciamo un breve ripasso di tutti i metodi e le proprietà più comuni degli Array, così da poter affrontare i vari use cases in maniera un pizzico più consapevole.

Generali

  • Dichiarazione di un array:
// Comunemente lo si dichiara con l'utilizzo delle parentesi quadre
let arrA = [item1, item2, item3, ...];

// oppure utilizzando il costruttore (usato molto raramente)
let arrB = new Array(item1, item2, item3, ...);
  • length – Indica la dimensione dell’Array (precisamente l’ultimo indice presente + 1);
  • Array.isArray(arr) – Verifica se il valore passato è un Array (dato che typeof tornerebbe come risultato object );
  • Array.from(obj[, mapFn, thisArg]) – Crea un’istanza Array da un Iterable o un array-like object;
  • Array.of() – Crea un’istanza Array da un numero variadico di parametri passati.

Modifiche

  • push(item1, item2, ...) – Aggiunge items alla fine dell’Array e ritorna il nuovo valore di length;
  • pop() – Rimuove e ritorna l’ultimo item alla fine dell’Array;
  • unshift(item1, item2, ...) – Aggiunge items all’inizio dell’Array e ritorna il nuovo valore di length;
  • shift() – Rimuove e ritorna l’ultimo item all’inizio dell’Array;
  • splice(p, n, item1, item2, ...) – Consente con un unico metodo di rimuovere n items a partire dalla posizione p e contemporaneamente aggiungere items nella stessa posizione p . Come valore di ritorno ha unArray con i valori eliminati. Una sorta di coltellino svizzero!
  • slice(start, end) – Crea, come valore di ritorno, un nuovo Array con gli elementi dell’array d’origine inclusi dalla posizione start sino alla posizione end (non inclusa);
  • concat(item1, item2, ...) – Crea, come valore di ritorno, un nuovo Array composto da tutti gli items dell’Array d’origine e gli items passati come parametri (che possono essere anche Array dei quali saranno accodati gli items);
  • arr.fill(value, start, end) – Popola l’Array ripetendo value dalla posizionestart allaend ;
  • arr.copyWithin(target, start, end) – Ricopia nella posizione target gli items dalla posizione start alla end (sovrascrivendo).

Ricerca

  • indexOf(item, pos) – Ritorna l’indice in cui viene trovato item partendo dalla posizione pos. Nel caso non venga trovato nessun item, sarà ritornato il valore -1. È anche presente il metodo lastIndexOf che ritorna l’ultimo indice trovato;
  • includes(item) – Ritorna true se l’array contiene item, altrimenti viene tornato il valore false;
  • filter(callback) – Crea, come valore di ritorno, un nuovo Array con gli items filtrati secondo la funzione callback passata come parametro (il cui risultato deve essere true);
  • find(callback) – Ritorna il valore del primo item trovato che soddisfa la condizione della funzione callback passata come parametro (il cui risultato deve essere true);
  • findIndex(callback) – Ritorna l’indice del primo item che soddisfa la condizione della funzione callback passata come parametro (il cui risultato deve essere true);
  • some(callback) – Ritorna true se l’Array contiene almeno un item che soddisfa la condizione della funzione callback passata come parametro (il cui risultato deve essere true);
  • every(callback) – Ritorna true se l’Array contiene tutti gli items che soddisfano la condizione della funzione callback passata come parametro (il cui risultato deve essere true) .

Iterazione

  • forEach(callback(item, index, array) – Esegue la funzione callback per ogni items dell’Array. Il metodo non ritorna nulla ma nella funzione callback è possibile utilizzare, oltre all’item corrente anche l’indice index e l’Array che stiamo ciclando.

Trasformazione

  • map(callback) – Crea, come valore di ritorno, un nuovo Array con i risultati della funzione callback eseguita per ogni items dell’Array d’origine;
  • sort(callback) – Ordina l’Array basandosi sulla conversione degli items in stringhe (e quindi in maniera lessicografica).
    Nel caso venga passata come parametro una funzionecallback sarà possibile definire un ordinamento custom;
  • reverse() – Inverte l’ordine degli items dell’Array;
  • reduce(callback, initial) – Ritorna un singolo valore dall’iterazione dei singoli items dell’Array, passando per singola iterazione alla funzione callback il valore intermedio tra le chiamate. Il valore intermedio sarà quindi utilizzato per accumulare il valore di ritorno;
  • flat(depth) – Utile per appiattire gli Array nidificati. Crea, come valore di ritorno, un nuovo Array con tutti gli items dell’Array d’origine, valutando il livello di nidificazione definito col parametro depth (che ha valore di default 1). È inoltre presente il metodo flatMap che consente di applicare un mapping degli items in fase di flattening (ed il valore di profondità è sempre pari ad 1).
  • join(glue) – Crea una stringa, come valore di ritorno, composta da tutti gli elementi dell’array concatenati dal valore del separatore glue .

Operazioni comuni

Spesso bisogna compiere operazioni comuni con i nostri Array ed esistono delle soluzioni rapide ed efficaci per farle, sfruttando molti dei metodi e delle proprietà che abbiamo appena visto.

Creare un Array da Elementi o Iterabili

// Da Elementi
const arrA = Array.of(1, 2, 3, 4, 5, 6);
console.log(arrA); // output: [1, 2, 3, 4, 5, 6]
// Da Array o Iterabili
const arrB = Array.from(document.querySelectorAll('li'));

Svuotare un Array

// Utilizzando il metodo splice
let arrA = ['Francesco', 'Andrea', 'Salvo'];
arrA.splice(0, arrA.length);
// Settando il valore della proprietà length a 0
let arrB = ['Francesco', 'Andrea', 'Salvo'];
arrB.length = 0

Aggiungere/Rimuovere Elementi (Queue/FIFO Mode)

let arr = [1, 2, 3, 4, 5];arr.shift() // output: [2, 3, 4, 5];
arr.push(6); // output: [2, 3, 4, 5, 6]

Aggiungere/Rimuovere Elementi (Stack/LIFO Mode)

let arr = [1, 2, 3, 4, 5];arr.push(6);  // output: [1, 2, 3, 4, 5, 6]
arr.pop(); // output: [1, 2, 3, 4, 5]

Inversione della posizione degli items

let arr = ["fs", 2, "50", 3];
arr.reverse() // output: [3, "50", 2, "fs"]

Ordinamento Lessicografico

let arr = ["fs", 2, "50", 3];
arr.sort(); // output: [2, 3, "50", "fs"]

Ordinamento Custom

let arr = [10, 4, 5, 74, 11];
arr.sort((el1, el2) => !(el1 - el2)); // output: [4, 5, 10, 11, 74]

Lavorare con gli insiemi

Come suggerisce la cara Wikipedia:

Un raggruppamento di oggetti rappresenta un insieme se esiste un criterio oggettivo che permette di decidere univocamente se un qualunque oggetto fa parte o no del raggruppamento.

Le operazioni tra gli insiemi sono comunemente non semplici da realizzare, e JavaScript per tanti anni non ci ha offerto soluzioni molto facili a riguardo.
Oggi invece, con l’aiuto di filter , concat , le arrow functions, lo spread operator e del built-in object Set possiamo eseguire le operazioni in una singola riga!

Intersezione (Items comuni tra gli array)

let arrA = [1,2,3];
let arrB = [2,5,3];
let intersectionArr = arrA.filter(value => arrB.includes(value));
console.log(intersectionArr); // output: [2,3]

Differenza (Items presenti solo sull’array filtrato)

let arrA = [1,2,3];
let arrB = [2,5,3];
let differenceArr = arrA.filter(value => !arrB.includes(value));
console.log(differenceArr); // output: [1]

Differenza Simmetrica (Items non comuni tra gli array)

let arrA = [1,2,3];
let arrB = [2,5,3];
let differenceArr = arrA
.filter(value => !arrB.includes(value))
.concat(arrB.filter(value => !arrA.includes(value)));
console.log(differenceArr); // output: [1,5]

Unione (Tutti gli items dei due array con eventuali duplicati)

let arrA = [1,2,3];
let arrB = [2,5,3];
let unionArr = [...arrA, ...arrB];
console.log(unionArr); // output: [1,2,3,2,5,3]

Unione (Tutti gli items dei due array senza duplicati)

let arrA = [1,2,3];
let arrB = [2,5,3];
let unionArr = [...new Set([...arrA, ...arrB)];
console.log(unionArr); // output: [1,2,3,5]

Iterazione

L’iterazione di un array è una delle operazioni più comuni ed esistono svariati tipi di costrutti che consentono di farlo.

Sicuramente tra le più utilizzate troviamo (in ordine di frequenza):

  • for (let item of arr) – Consente di ottenere il valore item per ogni iterazione dell’array arr ;
  • for (let i = 0; i < arr.length; i++) – Il ciclo for canonico per eccellenza!
  • for (let key in arr) – Consente di ottenere la chiavekey per ogni iterazione dell’array arr . Questo costrutto non è quasi mai utilizzato.

Mutare gli Array

Inserire, rimuovere e modificare item degli array è qualcosa di molto comune ed è estremamente facile farlo!

Creare un Array popolato con valori statici

// Con il metodo from (passando un array-like object)
let arrB = Array.from({length:10},()=> 10);
// Con lo spread operator, il costruttore Array ed il metodo map
let arrC = [...new Array(10)].map(()=> 10);
// Con il costruttore Array ed metodo fill
// (da evitare preferendo le altre forme su indicate)
let arrA = new Array(10).fill(10);

Rimuovere Items da posizioni specifiche

let arr = ["Ciao", "da", "Francesco", "Sciuti"];

arr.splice(1, 1);
console.log(arr); // output: ["Ciao", "Francesco", "Sciuti"];

Sostituire Items in posizioni specifiche

let arr = ["Ciao", "da", "Francesco", "Sciuti"];arr.splice(0, 2, "io", "sono");
console.log(arr) // output: ["io", "sono", "Francesco", "Sciuti"];

Inserire Items in posizioni specifiche

let arr = ["io", "sono", "Francesco", "Sciuti"];

arr.splice(2, 0, "il", "sig.");
console.log(arr); // output: ["io", "sono", "il", "sig.", "Francesco", "Sciuti"];

Immutabilità

L’immutabilità è un principio fondamentale nella programmazione funzionale.

Sappiamo però che in JavaScript in fase di assegnazione le variabili di tipo Array (ed anche gli Oggetti) vengono passate per riferimento e quindi quello che può accadere è che più variabili possano puntare alla stessa struttura dati!
Di seguito, effettueremo alcune operazioni che ci aiuteranno invece a mantenere immutabili gli array.

Clonare un Array

const sourceArray = [1,2,3];// Via spread operator
const clonedArray = [...sourceArray];
// Via metodo slice
const clonedArray = sourceArray.slice();

Aggiungere/Rimuovere Items (Stack o Queue)

function immutablePush(arr, newEntry){
return [ ...arr, newEntry ]
}
function immutablePop(arr){
return arr.slice(0, -1)
}
function immutableShift(arr){
return arr.slice(1)
}
function immutableUnshift(arr, newEntry){
return [ newEntry, ...arr ]
}

Ordinamento e Inversione della posizione degli Items

function immutableSort(arr, compareFunction) {
return [ ...arr ].sort(compareFunction)
}
function immutableReverse(arr) {
return [ ...arr ].reverse()
}

CRUD — Aggiungere un Item

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
const member = { id: 4, name: "Peppe", age: 26, skill: "frontend" };
let newTeam = [...team, member];

CRUD — Eliminazione di un Item

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
// Eliminazione via proprietà
const id = 2;
newTeam = team.filter(m => m.id !== id)
// Eliminazione via indice della posizione
const index = 1;
newTeam = [...team.slice(0, index), ...team(index + 1)];

CRUD — Modifica/Sostituzione di un Item

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
// Modifica di un Item (via map method e spread operator)
const updatedMember = { id: 1, age: 41 };
newTeam = team.map(m => m.id === updatedMember.id ? { ...m, ...updatedMember } : m);
// Sostituzione di un Item
const memberToReplace = { id: 3, name: "Nico", age: 30, skill: "frontend" };
// Non valutando la posizione precedente
const newTeam = [...team.filter(member => member.id !== memberToReplace.id), memberToReplace];
// Valutando la posizione precedente
const indexOldItem = team.findIndex(({ id }) => id == memberToReplace.id);
const newArray = [...team.slice(0, indexOldItem), memberToReplace, ...team.slice(indexOldItem + 1)]

Tips & Tricks

Alcuni casi particolari che possono sembrare complessi ma che possiamo risolvere con pochissimo sforzo.

Eseguire una funzione per ogni Item

const arr = [3, 1, 3, 5];
const mappedArr = arr.map((item) => i * 2);
console.log(mappedArr);
// output: [6, 2, 6, 10]

Rimuovere i duplicati

const arr = [3, 1, 3, 5, 2, 4, 4, 4];
const uniqueValuesArr = [...new Set(arr)];
console.log(uniqueValuesArr);
// output: [3, 1, 5, 2, 4]

Ricerca Case-Sensitive

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
let res = team.filter(member => member.skill.includes('end'));
console.log(res);
// output: [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" }
];

Ricerca Case-Insensitive

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
let res = team.filter(member => new RegExp('Cic', "i").test(member.name));
console.log(res);
// output: [{ id: 1, name: "Ciccio", age: 42, skill: "frontend" }];

Verifica se almeno un item supera una condizione

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
const hasFrontend = team.some(member => member.skill === 'frontend');
console.log(hasFrontend);
// output: true

Verifica se tutti gli elementi superano una condizione

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];

// all elements are greater than 4
const memberGreaterTirthy = team.every(num => num > 30);
console.log(memberGreaterTirthy);
// output: false

Flattening di Array

//Flattening con metodo Reduce
const nestedArr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
let flatArr = nestedArr.reduce((acc, it) => [...acc, ...it], []);
console.log(flatArr);
// output: [1, 2, 3, 4, 5, 6, 7, 8, 9]//Flattening con metodo Flat
let flatMethodArr = nestedArr.flat();
console.log(flatMethodArr);
// output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Cardinalità degli Items

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
const teamByAge = team.reduce((acc, item) => {
acc[item.age] = acc[item.age] + 1 || 1;
return acc;
}, {});
console.log(teamByAge);
// output: {42: 1, 36: 2}

Estrarre i valori unici di un array di una specifica chiave degli items

const team = [
{ id: 1, name: "Ciccio", age: 42, skill: "frontend" },
{ id: 2, name: "Andrea", age: 36, skill: "backend" },
{ id: 3, name: "Salvo", age: 36, skill: "devops" }
];
const listOfSkills = [...new Set(team.map(item => item.skill))];
console.log(listOfSkills);
// output: ['frontend', 'backend', 'devops'];

Detect di un Array

let arrA = ['Francesco', 'Andrea', 'Salvo'];console.log(Array.isArray(arrA)); // output: true

Conclusioni

Mi auguro che questo piccolo decalogo (o cookbook?) possa aiutarvi nei momenti difficili in cui vorreste lanciare il computer in aria per colpa degli Array o quando dovrete semplicemente affrontare uno dei casi elencati.

Per suggerimenti (o aggiunte perché no!), critiche, insulti, lodi, donazioni di enormi quantità di denaro, partite a padel o consigli non serve altro che scrivere nei commenti!

Un ringraziamento particolare a Salvo Pappalardo e Andrea Costa per la revisione dell’articolo. :)

Segui Acadevmy — Software Factory & Learning su Medium, Twitter e Facebook per contattarci o tenerti aggiornato sul mondo dello sviluppo Frontend e DevOps.

--

--

Francesco Sciuti
Devmy
Editor for

CEO@Acadevmy, Google Certified Developer, Projects Manager, Software Engineer, Speaker/Evangelist/Trainer