Los Map
en Javascript son estructuras de datos nativas que permiten implementar una estructura de tipo mapa, es decir, una estructuras donde tiene valores guardados a través de una clave para identificarlos. Comúnmente, esto se denomina pares clave-valor.
const map = new Map(); // Map({}) (Mapa vacío)
const map = new Map([[1, "uno"]]); // Map({ 1=>"uno" })
const map = new Map([[1, "uno"], [2, "dos"], [3, "tres"]]); // Map({ 1=>"uno", 2=>"dos", 3=>"tres" })
map.constructor.name; // "Map"
En este ejemplo, creamos un elemento map
, que no es más que un mapa de pares clave-valor. El primer map
se define como un mapa vacío, el segundo, es un mapa con un solo elemento, y el tercero con 3 elementos. Para inicializar los mapas con datos, se introduce como parámetro un array de entradas (un array de arrays), que en nuestro tercer caso tiene estas combinaciones:
- Clave:
1
=> Valor:"uno"
- Clave:
2
=> Valor:"dos"
- Clave:
3
=> Valor:"tres"
Por lo tanto, si consultamos map
con la clave 2
, nos devolverá un "dos"
.
¿Qué son los Map?
Los tipos de dato Map
son muy similares a los Objetos de Javascript, ya que estos últimos se pueden usar como estructuras de diccionario mediante pares clave-valor. Sin embargo, los Object
tienen algunas diferencias como que pueden colisionar algunos nombres de claves o que las claves deben ser
Una estructura de tipo Map
tiene las siguientes propiedades o métodos:
Propiedad o Método | Descripción |
---|---|
.size | Propiedad que devuelve el número de elementos que tiene el mapa. |
.set(key, value) | Establece o modifica la clave key con el valor value . Muta |
.has(key) | Comprueba si key ya existe en el mapa y devuelve si existe o no. |
.get(key) | Obtiene el valor de la clave key del mapa. |
.delete(key) | Elimina el elemento con la clave key del mapa. Devuelve si lo eliminó correctamente. |
.clear() | Vacía el mapa completamente. |
Vamos a analizar los diferentes métodos y propiedades que tienen los mapas.
Propiedad size
Si quieres saber cuántos elementos tiene un mapa, puedes utilizar la propiedad .size
, que funciona de forma muy similar al .length
de los array, por ejemplo.
const map = new Map();
map.size; // 0
const map = new Map([[1, "uno"], [2, "dos"]]);
map.size; // 2
const map = new Map([[1, "uno"], [2, "dos"], [1, "tres"]]);
map.size; // 2 (El 1->"tres" sobreescribe al anterior)
Observa que si introducimos un nuevo par clave-valor que tiene la misma clave que otro (tenemos dos que comparten la clave 1
), se sobreescribirá. No pueden existir dos pares clave-valor con la misma clave.
Métodos
Veamos ahora los diferentes métodos que tienen las estructuras de conjuntos Map
.
Establecer elementos
El método .set()
fija un par clave-valor en el mapa. Observa que hay un pequeño matiz muy importante de diferencia entre el concepto «añadir» (.add()
) y el concepto «establecer» o «fijar» (.set()
):
- Si usamos
.set()
para una clave que no existe, se añade al mapa. - Si usamos
.set()
para una clave que ya existe, la sobreescribe.
const map = new Map();
map.set(5, "cinco");
map.set("A", "letra A");
map.set(5, "cinco sobreescrito"); // Sobreescribe el anterior
map; // Map({ 5=>"cinco sobreescrito", "A"=>"letra A" })
Ten en cuenta que al contrario que los
Comprobar si existen
Para comprobar si un elemento existe en un mapa, se debe hacer a través de su clave, y se utiliza el método .has()
.Este método devuelve un true
, y en caso contrario, nos devolverá false
.
const map = new Map([[1, "uno"], [2, "dos"], [3, "tres"]]);
map.has(2); // true
map.has(34); // false
map.set(34, "treinta y cuatro");
map.has(34); // true
Recuerda que si estás utilizando tipos de datos más complejos como
Borrar elementos
Si necesitamos borrar algún elemento del mapa, lo podemos hacer mediante el método .delete()
. Devuelve un true
si lo consigue eliminar, en caso contrario, devolverá false
.
const map = new Map([[1, "uno"], [2, "dos"], [3, "tres"]]);
map.delete(3); // true
map.delete(39); // false
map; // Map({ 1=>"uno", 2=>"dos" })
Vacíar conjunto
Por último, utilizando el método .clear()
borraremos todos los elementos del mapa, dejándolo vacío. Este método no devuelve nada.
const map = new Map([[1, "uno"], [2, "dos"], [3, "tres"]]);
map.clear();
map.size; // 0
Convertir a Arrays
Si tenemos claro el proceso de desestructuración, podemos convertir los Map
en
const map = new Map([[1, "uno"], [2, "dos"], [3, "tres"]]);
map.size; // 3 (Contiene 3 elementos)
map.constructor.name; // "Map"
const entries = [...structuredClone(map)];
entries.constructor.name; // "Array"
entries; // [[1, "uno"], [2, "dos"], [3, "tres"]]
Recuerda utilizar
structuredClone()
para clonar la estructura si tiene elementos anidados, ya que sino sólo realizará una copia superficial y utilizará referencias para los elementos anidados.
Este array de entradas que nos da como resultado, lo podríamos utilizar para crear un nuevo map, o incluso un objeto:
const map = new Map(entries);
map; // Map({ 1=>"uno", 2=>"dos", 3=>"tres" })
const object = Object.fromEntries(entries);
object; // { 1: "uno", 2: "dos", 3: "tres" }
Aún así, recuerda que habría ciertos
Map
que quizás podrían dar conflictos al pasar a objeto, como por ejemplo, si tienes una clavetoString
. Más adelante comentaremos las diferencias.
¿Qué son los WeakMap?
Al igual que ocurre con los Set
y los WeakSet
, con los Map
tenemos una estructura denominada WeakMap
. La idea es la misma: se trata de una estructura derivada, muy similar a los Map
, pero con algunas diferencias.
Diferencias con los Map
Al margen de algunas diferencias que detallaremos más adelante, la diferencia principal de los Map
con los WeakMap
es que estos últimos, no permiten utilizar tipos primitivos (Map
si lo permite:
// *** Map
const map = new Map([[1, "uno"]]); // OK
const map = new Map([[{ id: 1, type: "number" }, "uno"]]); // OK
// *** WeakMap
const map = new WeakMap([[1, "uno"]]);
// ERROR: Uncaught TypeError: Invalid value used in weak map key
const map = new WeakMap([[{ id: 1, type: "number" }, "uno"]]); // OK
La razón de utilizar WeakMap
en lugar de Map
, es porque los primeros utilizan referencias débiles a un objeto, o lo que es lo mismo, si ese objeto no se utiliza (no está referenciado) en ninguna otra parte del código, se eliminará del WeakMap
automáticamente y en cuanto el Garbage Collector (Recolector de basura) lo decida, lo borrará de memoria.
Tabla de resumen de diferencias
A continuación, una tabla resumen de las diferencias entre Map
, WeakMap
y Object
:
Característica | Map | WeakMap | Object |
---|---|---|---|
Se pueden insertar claves repetidas | ❌ | ❌ | ❌ |
Se pueden insertar claves con tipos primitivos | ✅ | ❌ | Sólo |
Si no se usa el elemento, se elimina del map | ❌ | ✅ | ❌ |
Se puede convertir a array (es iterable) | ✅ | ❌ | ❌ Object.entries(obj) |
Pueden colisionar algunas claves * | ❌ | ❌ | ✅ |
Las claves garantizan un orden por inserción | ✅ | ✅ | ❌ |
Propiedad .size | ✅ | ❌ | ❌ Object.keys(obj).length |
Método .set() | ✅ | ✅ | ❌ Se usa asignación por clave |
Método .get() | ✅ | ✅ | ❌ Se usa acceso a la clave |
Método .has() | ✅ | ✅ | ❌ Object.keys(obj).includes(key) |
Método .delete() | ✅ | ✅ | ❌ |
Método .clear() | ✅ | ❌ | ❌ |
Respecto a lo que se menciona de que pueden colisionar algunas claves, es debido a que los
Observa el siguiente ejemplo:
const object = {};
object.toString = 42;
object.toString(); // ERROR: obj.toString is not a function
const map = new Map([["toString", 4]]);
map.toString(); // OK: "[object Map]"
Aquí se puede ver, como almacenando una propiedad llamada toString
en el objeto, colisiona con el método ya existente .toString()
, sobreescribiéndolo y dando problemas a la hora de convertir el objeto a