Map y Set en JavaScript: colecciones con ventajas sobre objetos y arrays

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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP