Map y Set son dos estructuras de datos introducidas en ES6 que resuelven limitaciones concretas de los objetos y arrays en JavaScript. No son simples alternativas estéticas: ofrecen semántica más clara, mejor rendimiento en operaciones específicas y funcionalidades imposibles con las estructuras básicas.
Map: objetos con cualquier tipo de clave
A diferencia de un objeto, un Map acepta cualquier valor como clave, incluyendo objetos, funciones y primitivos. Además, mantiene el orden de inserción y expone su tamaño directamente:
const mapa = new Map();
// Claves de cualquier tipo
mapa.set('texto', 'valor1');
mapa.set(42, 'valor2');
mapa.set(true, 'valor3');
const claveObjeto = { id: 1 };
mapa.set(claveObjeto, 'datos del objeto');
console.log(mapa.get('texto')); // 'valor1'
console.log(mapa.get(42)); // 'valor2'
console.log(mapa.get(claveObjeto)); // 'datos del objeto'
console.log(mapa.size); // 4
// Verificar existencia
console.log(mapa.has('texto')); // true
console.log(mapa.has('otro')); // false
// Eliminar y limpiar
mapa.delete(42);
console.log(mapa.size); // 3
mapa.clear();
console.log(mapa.size); // 0
Iterar sobre un Map
Map es iterable y proporciona métodos específicos para recorrer claves, valores y entradas:
const inventario = new Map([
['teclado', 15],
['ratón', 3],
['monitor', 7],
['auriculares', 0]
]);
// Iterar entradas (orden de inserción garantizado)
for (const [producto, stock] of inventario) {
console.log(`${producto}: ${stock}`);
}
// Métodos de iteración
console.log([...inventario.keys()]); // ['teclado', 'ratón', ...]
console.log([...inventario.values()]); // [15, 3, 7, 0]
console.log([...inventario.entries()]); // [['teclado', 15], ...]
// Filtrar con spread + filter
const agotados = [...inventario].filter(([, stock]) => stock === 0);
console.log(agotados); // [['auriculares', 0]]
// Convertir a y desde objeto
const obj = Object.fromEntries(inventario);
const mapaDesdeObj = new Map(Object.entries(obj));
Map vs objeto: cuándo usar cada uno
Los objetos son mejores para registros con estructura fija; Map es mejor cuando las claves son dinámicas, de tipo no-string, o cuando necesitas operar sobre el mapa como colección:
// Caso ideal para Map: contador de frecuencias
function contarPalabras(texto) {
const contador = new Map();
texto.toLowerCase().split(/s+/).forEach(palabra => {
contador.set(palabra, (contador.get(palabra) ?? 0) + 1);
});
return contador;
}
const freq = contarPalabras('el gato y el perro y el gato');
console.log(freq.get('el')); // 3
console.log(freq.get('gato')); // 2
// Ordenar por frecuencia:
const ordenado = [...freq.entries()].sort(([, a], [, b]) => b - a);
console.log(ordenado); // [['el', 3], ['gato', 2], ...]
Set: colección de valores únicos
Set almacena valores únicos de cualquier tipo. La búsqueda y la inserción son O(1), igual que en un Map:
const set = new Set([1, 2, 3, 2, 1, 4]); console.log(set.size); // 4 (duplicados eliminados) console.log([...set]); // [1, 2, 3, 4] // Operaciones básicas set.add(5); set.add(3); // Ya existe, no se añade set.delete(1); console.log(set.has(2)); // true console.log(set.has(1)); // false // Eliminar duplicados de un array (la operación más frecuente) const duplicados = [1, 2, 2, 3, 3, 3, 4]; const unicos = [...new Set(duplicados)]; console.log(unicos); // [1, 2, 3, 4] // Con strings const etiquetas = ['js', 'web', 'js', 'frontend', 'web', 'js']; const unicas = [...new Set(etiquetas)]; console.log(unicas); // ['js', 'web', 'frontend']
Operaciones de conjuntos con Set
Set no tiene métodos nativos de unión, intersección o diferencia (aunque llegarán en ES2025), pero son simples de implementar:
const setA = new Set([1, 2, 3, 4, 5]); const setB = new Set([3, 4, 5, 6, 7]); // Unión (todos los elementos) const union = new Set([...setA, ...setB]); console.log([...union]); // [1, 2, 3, 4, 5, 6, 7] // Intersección (elementos en ambos) const interseccion = new Set([...setA].filter(x => setB.has(x))); console.log([...interseccion]); // [3, 4, 5] // Diferencia (en A pero no en B) const diferencia = new Set([...setA].filter(x => !setB.has(x))); console.log([...diferencia]); // [1, 2] // Subconjunto: ¿es A subconjunto de B? const esSubconjunto = [...setA].every(x => setB.has(x)); console.log(esSubconjunto); // false
La regla práctica: elige Map sobre un objeto cuando las claves no son siempre strings, cuando el tamaño cambia frecuentemente, o cuando iterar sobre las entradas es una operación habitual. Elige Set cuando necesitas garantizar unicidad o hacer operaciones de conjuntos con búsquedas eficientes.
