Objeto `Set`

Este artículo explica el objeto Set.

Explicaremos el objeto Set con ejemplos prácticos.

YouTube Video

Objeto Set

Set es un objeto incorporado que se utiliza para manejar colecciones de valores únicos, sin duplicados. Permite escribir eliminación de duplicados y comprobaciones de existencia de manera más simple que con los arrays, y facilita la implementación de operaciones de conjuntos como unión e intersección.

Conceptos básicos: Creación y uso de Sets

Primero, veamos cómo crear un Set, agregar y eliminar elementos, comprobar la existencia y obtener su tamaño.

A continuación se muestra un patrón básico que crea un nuevo Set y demuestra add, has, delete y 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]
  • Como se muestra en este código, Set elimina automáticamente los valores primitivos duplicados, y puedes obtener el número de elementos usando size.

Métodos de iteración

Set es iterable, así que puedes recorrerlo usando for...of o forEach. El orden es el orden de inserción.

Aquí hay formas típicas de usar for...of y 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 de la función callback para forEach es value, value, set (por compatibilidad con Map), pero en la práctica, normalmente solo necesitas el primer argumento value.

Conversión entre Arrays y Sets (útil para eliminar duplicados)

Aquí mostramos una técnica sencilla para eliminar duplicados de un array y cómo convertir un Set de nuevo en un array.

A continuación se muestra un ejemplo de cómo eliminar duplicados de un array pasándolo por 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]
  • Este patrón es corto y rápido, por lo que se utiliza con frecuencia para eliminar duplicados de arrays. Es especialmente efectivo para valores primitivos.

Objetos y manejo de referencias

Los objetos en un Set se comparan por referencia, por lo que diferentes instancias con el mismo contenido se tratan como elementos separados.

El siguiente código demuestra qué sucede cuando agregas objetos 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 detección de duplicados para objetos se basa en la identidad de referencia, por lo que si deseas eliminar duplicados según el contenido del objeto, tendrás que serializarlos o procesarlos de otra manera.

Valores especiales: manejo de NaN y -0/+0

Set utiliza la regla de comparación Same-value-zero para determinar la igualdad de valores. Este método de comparación presenta las siguientes características respecto a los números:.

  • NaN se considera igual a NaN.
  • +0 positivo y -0 negativo no se distinguen y se tratan como el mismo valor.

Por lo tanto, cuando se agregan estos valores a un Set, ocurre el siguiente comportamiento:.

 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)
  • En una comparación normal (NaN === NaN), devuelve false, pero dentro de un Set, todos los valores NaN se consideran 'el mismo valor'.
  • +0 y -0 pueden distinguirse matemáticamente, pero en un Set, simplemente se tratan como 0.
  • Como resultado, solo queda un NaN y un 0 en el Set.
  • La regla de comparación de Set es similar a la de Object.is, pero no es exactamente la misma. Object.is(+0, -0) devuelve false, pero en un Set, se consideran idénticos. Ten en cuenta esta diferencia.

Utilidad común: operaciones de conjuntos (unión, intersección, diferencia)

Las operaciones de conjuntos se pueden escribir de manera más clara usando Set. A continuación se muestran ejemplos comunes de implementación.

Aquí tienes ejemplos de funciones para union, intersection y difference.

 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]
  • Las operaciones de conjunto se pueden escribir fácilmente usando filtros con la combinación de Set y arrays. Cuando se trabaja con grandes conjuntos de datos, el rendimiento O(1) de has hace que las operaciones sean más rápidas.

Ejemplo práctico: encontrar diferencias entre arrays (detectar elementos añadidos/eliminados)

El siguiente ejemplo demuestra cómo usar un Set para encontrar la diferencia entre dos arrays (una lista antigua y una nueva). Esto te permite identificar qué elementos fueron añadidos y cuáles fueron eliminados.

 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] }
  • Este método es muy conveniente para detectar diferencias en listas de ID, listas de etiquetas y situaciones similares. Es más sencillo de usar con valores primitivos.

Diferencias entre WeakSet y Set (gestión de memoria)

WeakSet es similar a Set, pero utiliza referencias débiles, lo que permite que sus elementos sean recolectados por el recolector de basura. A continuación se demuestran usos básicos de 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 solo puede contener objetos y no puede ser iterado. A continuación se muestran ejemplos de las limitaciones de WeakSet: solo almacena objetos y no puede iterarse.

 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 es útil para rastrear temporalmente la presencia de objetos, pero no puedes enumerar sus elementos ni obtener su tamaño.

Rendimiento y elección de uso

Al decidir si utilizar un Set, es importante entender sus características de rendimiento y la naturaleza de tus datos.

  • has, add y delete normalmente operan con un rendimiento prácticamente O(1) en promedio. Por lo tanto, en escenarios donde se verifican existencias o se eliminan duplicados con frecuencia, Set suele ser más ventajoso que los arrays.
  • Ten cuidado si quieres eliminar duplicados de objetos según su contenido (valores). Como Set compara por referencia, una forma práctica es usar IDs u otras claves, o serializar los objetos a valores primitivos antes de usar Sets cuando se necesita una comparación basada en valores.
  • Set es especialmente útil para mejorar la legibilidad del código en colecciones pequeñas o medianas. Por otro lado, si manejas una gran cantidad de elementos o haces conversiones frecuentes entre arrays y Sets, se recomienda realizar pruebas y mediciones reales.

Errores comunes

Set es conveniente, pero si no conoces bien sus especificaciones, puedes confundirte con comportamientos inesperados. Aquí algunos puntos típicos que debes tener en cuenta:.

  • Los objetos se comparan por referencia, por lo que aunque su contenido sea igual, objetos distintos no se consideran duplicados.
  • Set mantiene el orden de inserción, pero no puedes acceder a los elementos por índice como en los arrays. Si deseas acceder por índice, primero convierte el Set en un array.
  • WeakSet no puede ser enumerado y solo puede almacenar objetos. Ten en cuenta que sus aplicaciones son limitadas.
  • NaN se trata como el mismo valor y +0 y -0 no se distinguen. Esto se debe a la regla de comparación Same-value-zero.

Resumen

Set es una estructura de datos conveniente que te permite manejar colecciones de valores únicos de forma intuitiva. Puedes usarlo para eliminar duplicados de arrays, realizar rápidas comprobaciones de existencia o implementar operaciones como unión e intersección con código simple y legible.

Por otro lado, dado que los objetos se comparan por referencia, necesitas medidas extras si quieres juzgar la igualdad por su contenido.

Al comprender estas características y utilizarlas adecuadamente, Set se convierte en una herramienta poderosa para mejorar la legibilidad y el mantenimiento del código.

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video