`Objeto Map`

Este artigo explica o objeto Map.

Vamos explicar passo a passo, desde operações básicas até exemplos práticos úteis em cenários do mundo real.

YouTube Video

Objeto Map

Map é uma coleção que armazena pares de chave-valor. É semelhante a um objeto, mas difere porque qualquer tipo, como objetos, funções ou primitivos, pode ser usado como chave, e a ordem de inserção é preservada.

Noções básicas sobre Map

Primeiro, vamos ver como criar um Map e realizar operações básicas.

O código a seguir é um exemplo mínimo que cria um mapa vazio, adiciona chaves e recupera valores.

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • Nesse código, você pode adicionar elementos com set, recuperar valores com get e verificar o número de elementos com size.
  • Map mantém a ordem de inserção, tornando-o adequado para processamentos que dependem da ordem.

Comportamento de set, get, has e delete

Aqui estão exemplos de operações típicas de leitura, escrita, verificação de existência e exclusão.

Com o código a seguir, você pode verificar os valores de retorno e os efeitos de cada método.

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • get retorna undefined se a chave não existir. has verifica a existência de uma chave, e delete remove uma chave.
  • Além disso, como set retorna o próprio map, é possível encadear métodos.

Qualquer tipo pode ser usado como chave (utilizando objetos como chaves)

Uma das principais vantagens do Map é que você pode usar objetos diretamente como chaves.

O exemplo a seguir mostra como associar valores em um Map utilizando objetos como chaves.

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • Ao usar objetos como chaves, é importante que eles sejam a mesma referência. Mesmo que seus conteúdos sejam iguais, objetos com referências diferentes são tratados como distintos e não são a mesma chave.

Iteração (laço)

Map preserva a ordem de inserção, por isso a iteração é frequentemente utilizada.

A seguir, mostramos como usar for...of, forEach e os métodos keys(), values() e entries().

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries() retorna um array de pares [chave, valor], que pode ser convertido em um array usando a sintaxe spread. Observe que o callback para forEach recebe argumentos na ordem valor, chave.

Convertendo entre Map e Object

Você pode converter um objeto existente em um Map, ou converter um Map para um objeto simples ou para um array.

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • O uso de Object.entries e Object.fromEntries facilita a conversão. No entanto, como as chaves de objetos são limitadas a strings ou símbolos, chaves que não são strings são perdidas ao converter de volta para um objeto.

Padrão prático: Contagem de frequência (Count Map)

Ao contar a frequência de elementos em um array, usar um Map simplifica o processo.

O código a seguir é um exemplo de uso de um map para contar a frequência de strings em um array e ordená-las.

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • Usar um Map permite verificar a existência e atualizar facilmente. Isso pode ser feito com objetos também, mas Map pode ser mais intuitivo ao trabalhar com chaves arbitrárias.

Observações sobre Map e JSON.stringify (Serialização)

JSON.stringify não pode serializar um Map diretamente. Se você precisa salvar um Map, primeiro deve convertê-lo.

O exemplo a seguir mostra como converter um Map em um array antes de transformá-lo em JSON, e como restaurá-lo.

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • Maps que precisam ser salvos ou transmitidos devem ser convertidos em arrays antes da serialização. Ao restaurar, use JSON.parse para converter em um array e depois transforme de volta em um Map.

Introdução ao WeakMap e como utilizá-lo

WeakMap se diferencia por suas chaves serem fracamente referenciadas (sujeitas à coleta de lixo).

É útil para manter cache ou metadados com objetos como chaves, liberando-os automaticamente quando o objeto chave é coletado.

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap não pode ser enumerado nem ter seu tamanho verificado, mas é útil para evitar vazamentos de memória.

Resumo

Map é uma coleção conveniente que, ao contrário dos objetos, permite qualquer tipo como chave e preserva a ordem de inserção. Ao entender tudo, das operações básicas ao uso avançado, você pode gerenciar dados de forma mais flexível e intuitiva. Usando Object e Map de forma adequada para o caso de uso, você pode melhorar muito a clareza e legibilidade do código.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video