Oggetto `Set`

Questo articolo spiega l'oggetto Set.

Spiegheremo l'oggetto Set con esempi pratici.

YouTube Video

Oggetto Set

Set è un oggetto integrato utilizzato per gestire collezioni di valori unici, senza duplicati. Permette di scrivere più facilmente l'eliminazione dei duplicati e il controllo dell'esistenza rispetto agli array e rende più semplice l'implementazione di operazioni insiemistiche come unione e intersezione.

Basi: Creazione e utilizzo dei Set

Innanzitutto, vediamo come creare un Set, aggiungere e rimuovere elementi, controllare l'esistenza e ottenerne la dimensione.

Di seguito è riportato un modello base che crea un nuovo Set e mostra l'uso di add, has, delete e size.

 1// Create a Set and demonstrate add, has, delete, and size
 2const s = new Set();
 3
 4s.add(1);
 5s.add(2);
 6s.add(2); // duplicate, ignored
 7
 8console.log(s.has(1)); // true
 9console.log(s.has(3)); // false
10
11s.delete(2);
12console.log(s.size); // 1
13
14console.log([...s]); // [1]
  • Come mostrato in questo codice, Set rimuove automaticamente i valori primitivi duplicati e puoi ottenere il numero di elementi usando size.

Metodi di iterazione

Set è iterabile, quindi puoi scorrerlo usando for...of oppure forEach. L'ordine è l'ordine di inserimento.

Ecco i modi tipici per utilizzare for...of e forEach.

 1// Iterate a Set with for...of and forEach
 2const s = new Set(['a', 'b', 'c']);
 3
 4for (const v of s) {
 5  console.log('for...of:', v);
 6}
 7
 8s.forEach((value, sameValue, setRef) => {
 9  // Note: second arg is same as first for Set API to match Map signature
10  console.log('forEach:', value);
11});
  • La firma del callback per forEach è value, value, set (per compatibilità con Map), ma in pratica di solito serve solo il primo argomento value.

Conversione tra Array e Set (utile per rimuovere i duplicati)

Qui mostriamo una tecnica semplice per rimuovere i duplicati da un array e come convertire un Set nuovamente in un array.

Di seguito è riportato un esempio di rimozione dei duplicati da un array passandolo attraverso un Set.

1// Deduplicate an array using Set
2const arr = [1, 2, 2, 3, 3, 3];
3const deduped = [...new Set(arr)];
4console.log(deduped); // [1, 2, 3]
5
6// Convert a Set to an array using Array.from
7const s = new Set([4, 5, 6]);
8const arrFromSet = Array.from(s);
9console.log(arrFromSet); // [4, 5, 6]
  • Questo schema è breve e veloce, quindi viene spesso usato per rimuovere i duplicati negli array. È particolarmente efficace per i valori primitivi.

Oggetti e gestione dei riferimenti

Gli oggetti in un Set vengono confrontati per riferimento, quindi diverse istanze con lo stesso contenuto vengono considerate elementi distinti.

Il codice qui sotto mostra cosa succede quando si aggiungono oggetti a un Set.

 1// Objects are compared by reference in a Set
 2const obj1 = { x: 1 };
 3const obj2 = { x: 1 };
 4
 5const s = new Set();
 6s.add(obj1);
 7s.add(obj2);
 8
 9console.log(s.size); // 2 (different references)
10console.log(s.has(obj1)); // true
11console.log(s.has({ x: 1 })); // false (different object)
  • La rilevazione dei duplicati per gli oggetti si basa sull'identità di riferimento, quindi se vuoi deduplicare in base solo al contenuto degli oggetti, dovrai serializzarli o elaborarli in altro modo.

Valori speciali: Gestione di NaN e -0/+0

Set utilizza la regola di confronto Same-value-zero per determinare l'uguaglianza dei valori. Questo metodo di confronto presenta le seguenti caratteristiche riguardo ai numeri:.

  • NaN è considerato uguale a NaN.
  • +0 positivo e -0 negativo non sono distinti e vengono trattati come lo stesso valore.

Pertanto, quando aggiungi questi valori a un Set, si verifica il seguente comportamento:.

 1// NaN and zero behavior in Set
 2const s = new Set();
 3
 4s.add(NaN);
 5s.add(NaN);
 6console.log(s.size); // 1 (NaN considered the same)
 7
 8s.add(+0);
 9s.add(-0);
10console.log(s.size); // still 2 (NaN + 0)
11console.log([...s]); // [NaN, 0] (order may vary but only one zero)
  • Nella comparazione normale (NaN === NaN) restituisce false, ma all'interno di un Set tutti i vari NaN vengono considerati 'lo stesso valore'.
  • +0 e -0 possono essere distinti matematicamente, ma in un Set vengono semplicemente trattati come 0.
  • Di conseguenza, solo un NaN e uno 0 rimangono all'interno del Set.
  • La regola di confronto di Set è simile a Object.is, ma non è esattamente la stessa. Object.is(+0, -0) restituisce false, ma in un Set vengono considerati identici. Tieni presente questa differenza.

Utilità comuni: Operazioni su Set (Unione, Intersezione, Differenza)

Le operazioni su insiemi possono essere scritte in modo più chiaro usando i Set. Di seguito sono riportati alcuni esempi tipici di implementazione.

Ecco alcuni esempi di funzioni per unione, intersezione e differenza.

 1// Set operations: union, intersection, difference
 2function union(a, b) {
 3  return new Set([...a, ...b]);
 4}
 5
 6function intersection(a, b) {
 7  return new Set([...a].filter(x => b.has(x)));
 8}
 9
10function difference(a, b) {
11  return new Set([...a].filter(x => !b.has(x)));
12}
13
14// Demo
15const A = new Set([1, 2, 3]);
16const B = new Set([3, 4, 5]);
17
18console.log('union', [...union(A, B)]); // [1,2,3,4,5]
19console.log('intersection', [...intersection(A, B)]); // [3]
20console.log('difference A\\B', [...difference(A, B)]); // [1,2]
  • Le operazioni su insiemi possono essere scritte semplicemente usando i filtri combinando Set e array. Quando si gestiscono grandi quantità di dati, la performance O(1) di has rende le operazioni più veloci.

Esempio pratico: Trovare le differenze tra array (individuare elementi aggiunti/rimossi)

L'esempio seguente mostra come utilizzare un Set per trovare la differenza tra due array (una vecchia lista e una nuova lista). Questo ti permette di identificare quali elementi sono stati aggiunti e quali rimossi.

 1// Find added and removed items between two arrays
 2function diffArrays(oldArr, newArr) {
 3  const oldSet = new Set(oldArr);
 4  const newSet = new Set(newArr);
 5
 6  const added = [...newSet].filter(x => !oldSet.has(x));
 7  const removed = [...oldSet].filter(x => !newSet.has(x));
 8
 9  return { added, removed };
10}
11
12const oldList = [1, 2, 3];
13const newList = [2, 3, 4, 5];
14
15console.log(diffArrays(oldList, newList));
16// { added: [4,5], removed: [1] }
  • Questo metodo è molto comodo per rilevare differenze in liste di ID, liste di tag e situazioni simili. È più semplice da utilizzare con valori primitivi.

Differenze tra WeakSet e Set (Gestione della memoria)

WeakSet è simile a Set, ma utilizza riferimenti deboli, permettendo agli oggetti al suo interno di essere raccolti dal garbage collector. Qui di seguito vengono mostrati gli utilizzi di base di WeakSet.

1// WeakSet basics (objects only, not iterable)
2const ws = new WeakSet();
3let obj = { id: 1 };
4ws.add(obj);
5
6console.log(ws.has(obj)); // true
7
8obj = null; // Now the object is eligible for GC; WeakSet won't prevent collection

WeakSet può contenere solo oggetti e non può essere iterato. Di seguito sono riportati esempi dei limiti di WeakSet—contiene solo oggetti e non può essere iterato.

 1// WeakSet basics (objects only, not iterable)
 2const ws = new WeakSet();
 3
 4// --- Only objects can be added ---
 5try {
 6	ws.add(1); // number
 7} catch (e) {
 8	console.log("Error: WeakSet can only store objects. Adding a number is not allowed.");
 9}
10
11try {
12	ws.add("text"); // string
13} catch (e) {
14	console.log("Error: WeakSet can only store objects. Adding a string is not allowed.");
15}
16
17// --- WeakSet is not iterable ---
18try {
19	for (const value of ws) {
20		console.log(value);
21	}
22} catch (e) {
23	console.log("Error: WeakSet is not iterable. You cannot use for...of to loop over its elements.");
24}
25
26// --- Cannot convert to array ---
27try {
28	console.log([...ws]);
29} catch (e) {
30	console.log("Error: WeakSet cannot be converted to an array because it does not support iteration.");
31}
32
33// The object becomes eligible for garbage collection
34let obj = { id: 1 };
35ws.add(obj);
36obj = null;
  • WeakSet è utile per tracciare temporaneamente la presenza di oggetti, ma non puoi enumerare i suoi elementi o ottenerne la dimensione.

Prestazioni e scelta dell'utilizzo

Quando si decide se utilizzare un Set, è importante comprendere le sue caratteristiche prestazionali e la natura dei dati.

  • has, add e delete di solito operano con una prestazione media quasi O(1). Pertanto, in scenari in cui controlli frequentemente l'esistenza o rimuovi i duplicati, Set è spesso più vantaggioso rispetto agli array.
  • Fai attenzione se vuoi deduplicare oggetti in base al loro contenuto (valori). Poiché Set confronta per riferimento, un approccio pratico consiste nell'usare ID o altre chiavi, oppure serializzare gli oggetti in valori primitivi prima di usare i Set quando è necessario il confronto sui valori.
  • Set è particolarmente utile per migliorare la leggibilità del codice con collezioni di piccole o medie dimensioni. D'altra parte, se gestisci un numero molto elevato di elementi o effettui frequenti conversioni tra array e Set, è consigliato eseguire realmente benchmark e test.

Errori comuni

Set è conveniente, ma se non conosci le sue specifiche potresti essere sorpreso da comportamenti inaspettati. Ecco alcuni punti tipici a cui prestare attenzione:.

  • Gli oggetti vengono confrontati per riferimento, quindi anche se il loro contenuto è uguale, oggetti diversi non sono considerati duplicati.
  • Set mantiene l'ordine di inserimento, ma non puoi accedere agli elementi tramite indice come negli array. Se vuoi usare l'accesso tramite indice, converti prima il Set in un array.
  • WeakSet non può essere enumerato e può contenere solo oggetti. Nota che le sue applicazioni sono limitate.
  • NaN viene trattato come lo stesso valore e +0 e -0 non vengono distinti. Questo è dovuto alla regola di confronto Same-value-zero.

Riepilogo

Set è una struttura dati comoda che permette di gestire collezioni di valori unici in modo intuitivo. Puoi usarlo per rimuovere duplicati da array, effettuare controlli rapidi di esistenza o implementare operazioni su insiemi come unione e intersezione con codice semplice e leggibile.

D'altra parte, poiché gli oggetti vengono confrontati per riferimento, sono necessarie misure aggiuntive se si vuole valutare l'uguaglianza in base al loro contenuto.

Comprendendo queste caratteristiche e usandole in modo appropriato, Set diventa una scelta potente per migliorare la leggibilità e la manutenibilità del codice.

Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.

YouTube Video