Aprende leyendo en orden

Map y Set — eliminar duplicados y conteos con clave

Aprende Set, que guarda cada valor una sola vez, y Map, que guarda pares clave-valor en orden de registro.

Reúne en un array las etiquetas de un producto, y la misma etiqueta aparece una y otra vez — saber cuántas hay realmente distintas exige comprobar con includes cada vez. Querer guardar un par clave-valor, como un contador de stock indexado por ID de producto, choca con el mismo muro usando solo arrays: harían falta dos arrays separados para los ID y las cantidades, alineados por posición, y las claves de un objeto se limitan a cadenas y symbol, así que un número no puede usarse tal cual como clave.

Este artículo cubre Set, que nunca guarda duplicados, y Map, que puede usar algo más que cadenas como claves.

Eliminar duplicados — construir un Set

Imagina que quieres saber cuántas etiquetas distintas están realmente en uso en una lista de productos etiquetados. En un array normal, la misma etiqueta aparece más de una vez, así que contarla tal cual infla el total. Necesitas una estructura de datos que guarde los valores sin duplicados desde el principio.

Un Set (una estructura de datos que guarda cada valor solo una vez) se construye pasando un array, como en new Set(tags). El new al principio es sintaxis que pones antes de crear una instancia nueva de un tipo fijo de estructura de datos, como Set o Map.

Como el mismo valor nunca añade un duplicado, con solo leer size sabes el número de valores distintos. Si dos valores cuentan como el mismo sigue la misma regla que ===.

Un Set no es un array, así que un index como uniqueTags[0] no te devolverá nada. Cuando lo necesites como array, conviértelo con spread syntax, como en [...uniqueTags].

El orden se mantiene exactamente como se añadieron los elementos por primera vez, así que el orden de primera aparición del array original se conserva sin cambios.

Un Set fusiona valores duplicados en uno
1.ºSaleAún no lotieneEntracomo 1.º2.ºNewAún no lotieneEntracomo 2.º3.ºSaleYa lotieneEl conteo noaumenta
No pasa nada cuando llega un valor que ya tiene. El orden en que se añadieron los elementos se mantiene exactamente igual.
const tags = ["Sale", "New", "Sale", "Free Shipping"];

// Pasa un array, y obtienes un Set sin duplicados
const uniqueTags = new Set(tags);
console.log(uniqueTags.size);              // 3

// Vuelve a convertirlo en array con spread syntax (se conserva el orden de adición)
console.log([...uniqueTags].join(", "));   // Sale, New, Free Shipping

// El array original queda intacto
console.log(tags.length);                  // 4

// También puedes empezar desde un Set vacío
const empty = new Set();
console.log(empty.size);                   // 0

Saca las etiquetas distintas realmente en uso, de un array de etiquetas de productos. tags ya está declarado.

① Muestra el conteo del array original.

② Construye una estructura de datos sin duplicados, y muestra su conteo.

③ Convierte el resultado deduplicado de nuevo a array, unido con ", ", y muéstralo.

④ Muestra el 2.º elemento del array de ③, confirmando que se conservó el orden.

(Si se ejecuta correctamente, aparecerá una explicación.)

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Añadir y eliminar valores — add, has y delete

A veces quieres registrar IDs de visitantes y contar a la misma persona como 1 sin importar cuántas veces vuelva. Guarda esto en un array, y cada adición significa comprobar con includes antes de push — cuantos más registros se acumulan, más tarda esa comprobación.

Un Set tiene add para añadir un valor, has para comprobar si tiene uno, delete para eliminar uno, y size para el conteo. add no hace nada cuando llega el mismo valor, así que nunca necesitas comprobar antes de añadir.

has devuelve true o false, y delete devuelve si realmente eliminó algo.

Cómo se mueve size con add y delete
add unID nuevoAún no lotienesize sube1add elmismo IDYa lotienesize sequeda igualdeleteun IDLo quita delregistrosize baja1
addir el mismo valor cualquier número de veces nunca aumenta el conteo. Solo eliminar lo reduce.
const visitors = new Set(["u-102", "u-337"]);

// Comprueba si lo tiene
console.log(visitors.has("u-102"));   // true
console.log(visitors.has("u-999"));   // false

// Añade uno. El mismo valor no cambia el conteo
visitors.add("u-541");
console.log(visitors.size);           // 3
visitors.add("u-541");
console.log(visitors.size);           // 3

// Elimina uno. Vuelve si realmente eliminó algo
console.log(visitors.delete("u-102"));  // true
console.log(visitors.delete("u-102"));  // false (ya se había ido)
console.log(visitors.size);             // 2

Opera sobre un registro de visitantes de hoy y confirma cómo cambia el conteo. visitors ya está declarado.

① Muestra si u-337 ya se ha registrado.

② Registra u-999, y muestra el conteo.

③ Registra ese mismo u-999 otra vez, y muestra el conteo.

④ Elimina u-102 del registro, y muestra si sigue ahí.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Guardar pares clave-valor — Map

A veces quieres guardar una clave y un valor como par, como un contador de stock por ID de producto. Un objeto también puede hacerlo, pero para contar claves que se deciden en tiempo de ejecución —como IDs— y ajustarlas con el tiempo, una estructura de datos con métodos dedicados es más adecuada.

Un Map (una estructura de datos que guarda pares clave-valor en el orden en que se registraron) se construye con new Map(), y registras entradas con set(key, value). Para leer una, usa get(key); para comprobar si existe, has(key); para el conteo, size.

Haz set sobre la misma clave otra vez, y el valor se sobrescribe. Para empezar con entradas ya rellenadas, pásale un array de pares, como en new Map([["P-001", 12], ["P-002", 5]]).

Un Map obtiene y fija pares por clave
set P-001y 12Lo registracomo parsize sube1get, dandoP-001Busca laclaveDevuelve elvalor, 12get, dandoP-999No seencuentraDevuelveundefined
set registra un par, y get busca un valor por clave. Una clave sin entrada devuelve undefined.
const stock = new Map();

// Registra un par con set
stock.set("P-001", 12);
stock.set("P-002", 5);
console.log(stock.size);            // 2

// Busca un valor por su clave con get
console.log(stock.get("P-002"));    // 5
console.log(stock.get("P-999"));    // undefined
console.log(stock.has("P-999"));    // false

// Haz set sobre la misma clave otra vez, y sobrescribe
stock.set("P-001", 8);
console.log(stock.get("P-001"));    // 8
console.log(stock.size);            // 2 (el conteo no crece)

// También lo puedes construir con pares ya rellenados
const prices = new Map([["P-001", 3980], ["P-002", 2480]]);
console.log(prices.get("P-001"));   // 3980

Los valores de un Map no se pueden leer con notación de punto

Escribe stock["P-001"] en vez de stock.get("P-001"), y no obtendrás el valor de vuelta. Esa sintaxis busca una propiedad en el propio objeto Map, que no tiene nada que ver con los pares que registraste, así que devuelve undefined. Pasa la lectura y escritura por get y set.

Construye una estructura de datos que guarde contadores de stock por ID de producto. stock ya está declarado, vacío.

① Registra P-001 como 12, P-002 como 5, y P-003 como 3, y luego muestra el conteo.

② Muestra el contador de stock de P-002.

③ Muestra el contador de stock del no registrado P-999.

④ Actualiza el stock de P-001 a 4, y muestra el valor después de actualizar.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Elegir entre Map y Object — tipo de clave y orden

Como guarda claves y valores igual que un objeto, eliges entre ellos. Cuando las claves están fijas en el momento en que escribes el código, como campos de configuración, un objeto funciona; cuando estás contando claves que crecen en tiempo de ejecución, como IDs o fechas, Map es más fácil de manejar.

El tipo de clave y el orden son en lo que basas esa decisión.

Las claves de un objeto se convierten a cadenas, así que incluso escribiendo record[2026], la clave real es "2026". Un Map usa el valor que le pases como clave exactamente tal cual, así que el número 2026 y la cadena "2026" se tratan como claves distintas.

El orden también funciona distinto — un Map siempre conserva el orden en que se registraron las cosas.

Un Map conserva el tipo de una clave exactamente igual
Registra conMap usando 2026Se queda comoclave numéricaget(2026)lo recuperaBusca en el mismoMap por cadenaSe trata comoclave distintaDevuelveundefinedRegistra conObject usando 2026Se conviertea cadenaUna cadenatambién lo recupera
Un Map trata el número 2026 y la cadena "2026" como claves distintas. Un objeto convierte ambos a cadenas.
AspectoObjectMap
Tipos permitidos como claveSe convierten a cadenasCualquier tipo, tal cual
OrdenLas claves tipo entero se ordenan primeroSe mantiene el orden de registro
Contar entradaslength de Object.keyssize
Leer y escribirobj.key / obj[key]get y set
Mejor paraUna colección fija de camposContar claves que crecen en tiempo de ejecución

Para listar el contenido de un Map, escribe [...stock.entries()] para convertirlo en un array de pares [clave, valor], cada uno un array de 2 elementos. Eso te deja sacar un par a la vez, como en entries[0]. Repetir el mismo paso una vez por cada par, sean los que sean, se cubre de nuevo en el artículo de bucles.

Cuando solo necesites las claves, usa stock.keys() — pero como lo que devuelve no es un array, no puedes encadenar join directamente sobre eso. Escribe [...stock.keys()] para convertirlo antes a array.

const scores = new Map([["math", 80]]);

// Un Map conserva una clave numérica como número
scores.set(2026, "this year's tally");
console.log(scores.get(2026));               // this year's tally
console.log(scores.get("2026"));             // undefined (una clave distinta)

// Las claves de un objeto se convierten a cadenas
const record = {};
record[2026] = "this year's tally";
console.log(Object.keys(record)[0]);         // 2026
console.log(typeof Object.keys(record)[0]);  // string
console.log(record["2026"]);                 // this year's tally (se trata como la misma clave)

// Convierte keys() / entries() a array con spread syntax antes de usarlos
console.log([...scores.keys()].join(", "));   // math, 2026

const pairs = [...scores.entries()];
console.log(pairs[0].join(": "));             // math: 80

// Un objeto ordena primero las claves de tipo entero
const ranking = {};
ranking[3] = "c";
ranking[1] = "a";
console.log(Object.keys(ranking).join(", "));  // 1, 3

Lista el contenido de un Map de stock, y luego confirma la diferencia que hace el tipo de clave. stock ya está declarado, con 3 pares registrados.

① Convierte solo las claves registradas en un array, y muéstralas unidas con ", ".

② Convierte los 3 pares en un array de pares [clave, valor], y muestra cada uno con el formato "clave: valor" en 3 líneas.

③ Registra "Annual Ranking" con el número 2026 como clave, y recupéralo con esa misma clave para mostrarlo.

④ Recupera la misma entrada con la cadena "2026" en su lugar, y muestra qué vuelve.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Con const ids = new Set(["u-1", "u-2"]);, ejecuta ids.add("u-1");. ¿Cuál es ids.size?

Pregunta 2Con stock como Map, ¿qué devuelve stock.get("P-999") para una clave no registrada?

Pregunta 3Registra el número 2026 como clave. ¿Qué diferencia hay entre Map y un objeto?