Original article: How to Use JavaScript Collections – Map and Set

En JavaScript, los objetos son usados para almacenar múltiples valores como un conjunto de datos complejos.

Un objeto es creado con llaves {...} y una lista de propiedades. Una propiedad es un par de clave-valor donde la clave debe ser una cadena y el valor puede ser de cualquier tipo.

Por otro lado, los arreglos son una colección ordenada que puede retener datos de cualquier tipo. En JavaScript, los arreglos son creados con corchetes [...] y permiten elementos duplicados.

Hasta ES6 (ECMAScript 2015), los objetos y los arreglos de JavaScript fueron las estructuras de datos más importantes para manejar colecciones de datos. La comunidad de desarrolladores no tenían muchas opciones fuera de eso. Aun así, una combinación de objetos y de arreglos permitía manejar datos en muchos escenarios.

Sin embargo, hubo unas pocas deficiencias:

  • Las claves del objeto pueden ser solo de tipo cadena.
  • Los objetos no mantienen el orden de los elementos que les son agregados.
  • Los objetos carecen de algunos métodos útiles, lo cual los hace más difícil de usar en algunas situaciones. Por ejemplo, no puedes computar el tamaño (longitud) de un objeto fácilmente. También, enumerar un objeto no es tan sencillo.
  • Los arreglos son colecciones de elementos que permiten duplicados. Hacer uso de arreglos que solamente tienen elementos distintos requiere lógica y código extra.

Con la introducción de ES6, recibimos dos estructuras de datos nuevas que abarcan las deficiencias mencionadas arriba: Map y Set. En este artículo, miraremos a ambos de cerca y entenderemos cómo usarlos en diferentes situaciones.

Map en JavaScript

Map es una colección de pares clave-valor donde la clave puede ser de cualquier tipo. Map recuerda el orden original en los cuales los elementos les fueron agregados, lo que significa que los datos pueden ser recuperados en el mismo orden en el que fueron insertados.

En otras palabras, Map tiene características tanto del Objeto como del Arreglo:

  • Como un objeto, soporta la estructura de par clave-valor.
  • Como un arreglo, recuerda el orden de inserción.

Cómo crear e inicializar un Map en JavaScript

Un nuevo Map puede ser creado así:

const map = new Map();

Lo cual regresa un Map vacío:

Map(0) {}

Otra forma de crear un Map es con valores iniciales. Así es cómo se crea un Map con tres pares de clave-valor:

const freeCodeCampBlog = new Map([
  ['nombre', 'freeCodeCamp'],
  ['tipo', 'blog'],
  ['escritor', 'Tapas Adhikary'],
]);

Lo cual regresa un Map con tres elementos:

Map(3) {"nombre" => "freeCodeCamp", "tipo" => "blog", "escritor" => "Tapas Adhikary"}

Cómo agregar valores a un Map en JavaScript

Para agregar un valor a Map, usamos el método set(clave, valor).

El método set(clave, valor) toma dos parámetros, clave y valor, donde la clave y el valor pueden ser de cualquier tipo, un primitivo (boolean, string, number, etc.) o un objeto:

// crea un map
const map = new Map();

// Agrega valores al map
map.set('nombre', 'freeCodeCamp');
map.set('tipo', 'blog');
map.set('escritor', 'Tapas Adhikary');

Salida:

Map(3) {"nombre" => "freeCodeCamp", "tipo" => "blog", "escritor" => "Tapas Adhikary"}

Por favor, ten en cuenta, si usas la misma clave para agregar un valor a un Map múltiples veces, siempre lo reemplazará con el valor anterior:

// Agregar un escritor distinto
map.set('escritor', 'Alguien más!');

Así que la salida sería:

Map(3) 
{"nombre" => "freeCodeCamp", "tipo" => "blog", "escritor" => "Alguien más!"}

Cómo obtener valores de un Map en JavaScript

Para obtener un valor de un Map, usamos el método get(clave):

map.get('nombre'); // regresa freeCodeCamp

Todo sobre las claves de Map en JavaScript

Las claves de Map pueden ser de cualquier tipo, un primitivo, o un objeto. Este es una de las mayores diferencias entre Map y los objetos de JavaScript regulares donde las claves pueden ser solamente una cadena:

// crea un Map
const funMap = new Map();

funMap.set(360, 'Mi numero de casa'); // number como clave
funMap.set(true, 'Escribo blogs!'); // boolean como clave

let obj = {'nombre': 'tapas'}
funMap.set(obj, true); // object como valor

console.log(funMap);

Esta es la salida:

Map(3) 
{
  360 => "Mi numero de casa", 
  true => "Escribo blogs!", 
  {…} => true
}

Un objeto de JavaScript regular siempre trata a la clave como una cadena. Inclusive cuando le pasas un primitivo o un objeto, internamente convierte a la clave en una cadena:

// Crea un objeto vacío
const funObj = {};

// agrega una propiedad. Fíjate, pasando la clave como un número.
funObj[360] = 'My House Number';

// Regresa verdadero porque el número 360 se convirtió en cadena '360' internamente!
console.log(funObj[360] === funObj['360']);

Propiedades y Métodos de Map en JavaScript

El Map de JavaScript tiene propiedades y métodos adheridos que lo hacen fácil de usar. Aquí hay algunos de los más comunes:

  • Usar la propiedad size para saber cuántos elementos hay en un Map.
  • Buscar un elemento con el método has(clave).
  • Remover un elemento con el método delete(clave).
  • Usar el método clear() para remover todos los elementos del Map de una sola vez.
console.log('el tamaño del map es', map.size);
// regresa verdadero, si el map tiene un elemento con la clave, 'John'
console.log(map.has('John')); 


// regresa falso, si el map no tiene un elemento con la clave, 'Tapas'
console.log(map.has('Tapas')); 
map.delete('Sam'); // remueve el elemento con la clave, 'Sam'.
// Limpia el map removiendo todos los elementos
map.clear(); 

map.size // Regresará 0

Iterador de Map: keys(), values(), and entries() en JavaScript

Los métodos keys(), values() y entries() regresan un MapIterator, lo cual es excelente porque puedes usarlo en un bucle for-of o forEach.

Primero, crea un Map simple:

const mapEdad = new Map([
  ['Jack', 20],
  ['Alan', 34],
  ['Bill', 10],
  ['Sam', 9]
]);
  • Obtiene todas las claves.
  • Obtiene todos los valores.
  • Obtiene todas las entradas (pares clave-valor).
console.log(ageMap.keys());

// Salida:

// MapIterator {"Jack", "Alan", "Bill", "Sam"}
console.log(edadMap.values());

// Salida

// MapIterator {20, 34, 10, 9}
console.log(ageMap.entries());

// Salida

// MapIterator {"Jack" => 20, "Alan" => 34, "Bill" => 10, "Sam" => 9}

Cómo iterar sobre un Map en JavaScript

Puedes usar cualquiera de los bucles forEach o for-of para iterar sobre un Map:

// con forEach
edadMap.forEach((value, key) => {
   console.log(`${key} tiene ${value} años!`);
});

// con for-of
for(const [key, value] of edadMap) {
  console.log(`${key} tiene ${value} años!`);
}

La salida va a ser el mismo en ambos casos:

Jack tiene 20 años!
Alan tiene 34 años!
Bill tiene 10 años!
Sam tiene 9 años!

Cómo convertir un Objeto en un Map en JavaScript

Podrías encontrarte con una situación donde necesitas convertir un objeto a una estructura de tipo Map. Puedes usar el método entries del Objeto para hacer eso:

const direccion = {
  'Tapas': 'Bangalore',
  'James': 'Huston',
  'Selva': 'Srilanka'
};

const mapDirecciones = new Map(Object.entries(direccion));

Cómo convertir un Map en un Objeto en JavaScript

Si lo quieres hacer a la inversa, puedes usar el método fromEntries:

Object.fromEntries(map)

Cómo convertir un Map en un Arreglo en JavaScript

Hay un par de maneras de convertir un map en un arreglo:

  • Usando Array.from(map):
  • Usando el operador spread:
const map = new Map();
map.set('leche', 200);
map.set("te", 300);
map.set('cafe', 500);

console.log(Array.from(map));
console.log([...map]);

Map vs. Object: ¿Cuándo deberías usarlo?

El Map tiene características de ambos Object y Array. Sin embargo, Map es más como un Object que un Array debido a la naturaleza de almacenar datos en el formato clave-valor.

Aunque la similitud con los objetos termina aquí. Como has visto, el Map es diferente en muchas maneras. Así que, ¿cuál deberías usar, y cuándo? ¿Cómo decides?

Usar Map cuando:

  • Tus necesidades no son tan sencillas. Puede que quieras crear claves que no sean cadena. Almacenar un objeto como una clave es un enfoque muy poderoso. Map te da esta habilidad por defecto.
  • Necesitas una estructura de datos donde los elementos pueden ser ordenados. Los objetos regulares no mantienen el orden de sus entradas.
  • Estás buscando flexibilidad sin depender de una librería externa como lodash. Podrías terminar usando una librería como lodash porque no encontramos métodos como has(), values(), delete(), o una propiedad como size con un objeto regular. Map lo hace fácil para ti proveyendo todos estos métodos por defecto.

Usar un objeto cuando:

  • No tienes ninguna de las necesidades listadas arriba.
  • Dependes de JSON.parse(), ya que con un Map no lo puedes convertir.

Set en JavaScript

Un Set es una colección de elementos únicos que pueden ser de cualquier tipo. Set es también una colección ordenada de elementos, lo que significa que esos elementos serán recuperados en el mismo orden en el que fueron insertados.

Un Set en JavaScript se comporta de la misma manera que un set matemático.

Cómo crear e inicializar un Set en JavaScript

Un nuevo Set puede ser creado así:

const set = new Set();
console.log(set);

Y la salida será un Set vacío:

Set(0) {}

Así es cómo se crea un Set con algunos valores iniciales:

const conjuntoFrutas = new Set(['?', '?', '?', '?']);
console.log(conjuntoFrutas);

Salida:

Set(4) {"?", "?", "?", "?"}

Las Propiedades y Métodos de Set en JavaScript

El Set tiene métodos para agregar un elemento, eliminar elementos, verificar si un elemento existe, y limpiarlo completamente:

  • Usa la propiedad size para saber el tamaño del Set. Regresa el número de elementos.
  • Usa el método add(elemento) para agregar un elemento al Set:
set.size
// Crea un set - setEnsalada
const setEnsalada = new Set();

// Agregar algunos vegetales
setEnsalada.add('?'); // tomate
setEnsalada.add('?'); // aguacate (o palta)
setEnsalada.add('?'); // zanahora
setEnsalada.add('?'); // pepino

console.log(setEnsalada);


// salida

// Set(4) {"?", "?", "?", "?"}

¡Me encantan los pepinos! ¿Qué te parece agregar uno más?

Oh, no, no puedo – Set es una colección de elementos únicos:

setEnsalada.add('?');
console.log(setEnsalada);

La salida es la misma que antes – nada fue agregado al setEnsalada.

  • Usa el método has(element) para buscar si tenemos una zanahoria (?) o brócoli (?) en el Set:
  • Usa el método delete(element) para quitar el aguacate (?) del Set:
// La ensalda tiene una ?, así que regresa verdadero
console.log('¿La ensalada tiene una zanahoria?', setEnsalada.has('?'));

// La ensalada no tiene un ?, así que regresa falso
console.log('¿La ensalda tiene brócoli?', setEnsalada.has('?'));
setEnsalada.delete('?');
console.log('No me gusta ?, quitar de la ensalada:', setEnsalada);

Ahora nuestra ensalada Set es así:

Set(3) {"?", "?", "?"}
  • Usa el método clear() para quitar todos los elementos de un Set:
setEnsalada.clear();

Cómo iterar sobre un Set en JavaScript

Set tiene un método llamado values() el cual regresa un SetIterator para obtener todos sus valores:

// Crea un Set
const nrosCasas = new Set([360, 567, 101]);

// Obtener el SetIterator usando el método `values()`
console.log(nrosCasas.values());

Salida:

SetIterator {360, 567, 101}

Podemos usar un bucle forEach o for-of para recuperar los valores.

Interesantemente, JavaScript intenta hacer a Set compatible con Map. Por eso encontramos dos de los mismos métodos como en Map, keys() y entries().

Ya que Set no tiene claves, el método keys() regresa un SetIterator para recuperar sus valores:

console.log(nrosCasas.keys());

// Salida

// console.log(nrosCasas.keys());

Con Map, el método entries() regresa un iterador para recuperar pares de clave-valor. De nuevo, no hay claves en un Set, así que entries() regresa un SetIterator para recuperar los pares de valor-valor:

console.log(nrosCasas.entries());

// Salida

// SetIterator {360 => 360, 567 => 567, 101 => 101}

Cómo enumerar sobre un Set en JavaScript

Podemos enumerar sobre un Set usando los bucles forEach y for-of:

// con forEach

nrosCasas.forEach((valor) => {
   console.log(valor);
});


// con for-of

for(const valor of nrosCasas) {
   console.log(valor);
 }

La salida de ambos es:

360
567
101

Sets y arreglos en JavaScript

Un arreglo, como un Set, te permite agregar y quitar elementos. Pero Set es muy diferente, y no está destinado para reemplazar los arreglos.

La mayor diferencia entre un arreglo y un Set es que los arreglos te permiten tener elementos duplicados. También, algunas de las operaciones de Set como delete() son más rápidos que las operaciones del arreglo como shift() o splice().

Piensa de Set como una extensión de un arreglo regular, solo que con más músculos. La estructura de datos Set no es un reemplazo del array. Ambos pueden resolver problemas interesantes.

Cómo convertir un Set en un arreglo en JavaScript

Convertir un Set en un arreglo es simple:

const arr = [...nrosCasas];
console.log(arr);

Valores únicos de un arreglo usando el Set en JavaScript

Crear un Set es una forma realmente sencilla para quitar valores duplicados de un arreglo:

// Crea un arreglo frutasMezcladas con unas pocas frutas duplicadas
const frutasMezcladas = ['?', '?', '?', '?', '?', '?', '?'];

// Pasa el arreglo para crear un conjunto de frutas únicas
const setFrutasMezcladas = new Set(frutasMezcladas);

console.log(setFrutasMezcladas);

Salida:

Set(4) {"?", "?", "?", "?"}

Set y Object en JavaScript

Un Set puede tener elementos de cualquier tipo, inclusive objetos:

// Crea un objeto de persona
const persona = {
   'nombre': 'Alex',
   'edad': 32
 };

// Crea un conjunto y le agrega el objeto
const setP = new Set();
setP.add(persona);
console.log(setP);

Salida:

image-113

No hay sorpresa aquí – el Set contiene un elemento que es un objeto.

Cambiemos una propiedad del objeto y agreguémosle al set de nuevo:

// Cambiar el nombre de la persona
persona.nombre = 'Bob';

// Agregar el objeto de persona al set otra vez
setP.add(persona);
console.log(setP);

¿Qué salida piensas que será? ¿Dos objetos persona o solo uno?

Aquí está la salida:

image-114

Set es una colección de elementos únicos. Cambiando la propiedad del objeto, no hemos cambiado el objeto mismo. Sin embargo, Set no permitirá duplicar elementos.

Set es una estructura de datos genial para usar en adición a los arreglos de JavaScript. Aunque no tiene una gran ventaja sobre los arreglos regulares.

Usa Set cuando necesites mantener un conjunto de datos distintivo para ejecutar operaciones de Set como union, intersection, difference, y así sucesivamente.

En Resumen

Aquí hay un repositorio de GitHub para encontrar todo el código fuente usado en este artículo. Si lo encontraste útil, por favor muestra tu apoyo dándole una estrellita: https://github.com/atapas/js-collections-map-set.

Te podría gustar también algunos de mis otros artículos:

Si este artículo te fue útil, por favor compártelo así otros lo pueden leer también. Puedes etiquetarme @ en Twitter (@tapasadhikary) con comentarios, o no dudes en seguirme.