Artigo original: How to Use JavaScript Collections – Map and Set

Em JavaScript, os objetos são usados para armazenar múltiplos valores como uma estrutura de dados complexa.

Um objeto é criado com chaves {...} e uma lista de propriedades. Uma propriedade é um par de chave-valor, onde a chave deve ser uma string e o valor pode ser de qualquer tipo.

Por outro lado, os arrays são uma coleção ordenada que pode conter dados de qualquer tipo. Em JavaScript, os arrays são criados com colchetes [...] e permitem a duplicação de elementos.

Até a ES6 (ECMAScript 2015), os objetos e arrays em JavaScript eram as estruturas de dados mais importantes para lidar com coleções de dados. A comunidade de desenvolvedores não tinha muitas opções além dessas duas. Mesmo assim, uma combinação de objetos e arrays era capaz de lidar com dados em muitos cenários.

No entanto, havia algumas falhas:

  • As chaves dos objetos só podiam ser do tipo string.
  • Os objetos não mantinham a ordem dos elementos inseridos neles.
  • Os objetos carecem de alguns métodos úteis, o que os torna difíceis de usar em algumas situações. Por exemplo, não se pode calcular facilmente o tamanho (length) de um objeto. Além disso, a enumeração de um objeto não é tão direta.
  • Os arrays são coleções de elementos que permitem duplicatas. O suporte a arrays que tenham apenas elementos distintos requer lógica e código adicionais.

Com a introdução do ES6, obtivemos duas novas estruturas de dados que abordam as falhas mencionadas acima: Map e Set. Neste artigo, analisaremos as duas estruturas de perto e entenderemos como utilizá-las em diferentes situações.

Map em JavaScript

O map é uma coleção de pares chave-valor onde a chave pode ser de qualquer tipo. O map lembra a ordem original em que os elementos foram adicionados a ele, o que significa que os dados podem ser recuperados na mesma ordem em que foram inseridos.

Em outras palavras, o map tem características tanto do objeto quanto do array:

  • Como um objeto, ele suporta a estrutura de pares chave-valor.
  • Como um array, ele se lembra da ordem de inserção.

Como criar e inicializar um map em JavaScript

Um map pode ser criado assim:

const map = new Map();

Isso retornará um map vazio:

Map(0) {}

Outro modo de criar um map é com valores iniciais. Veja como criar um map com três pares chave-valor:

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

Isso retornará um map com três elementos:

Map(3) {"nome" => "freeCodeCamp", "tipo" => "blog", "autor" => "Tapas Adhikary"}

Como adicionar valores a um map em JavaScript

Para adicionar valor a um map, use o método set(chave, valor).

O método set(chave, valor) toma dois parâmetros, chave e valor, onde a chave e o valor podem ser de qualquer tipo, um primitivo (booleano, string, número etc.) ou um objeto:

// Cria um map
const map = new Map();

// Adiciona valores ao map
map.set('nome', 'freeCodeCamp');
map.set('tipo', 'blog');
map.set('autor', 'Tapas Adhikary');

Resultado:

Map(3) {"nome" => "freeCodeCamp", "tipo" => "blog", "autor" => "Tapas Adhikary"}

Observe que, se você usar a mesma chave para adicionar um valor a um map várias vezes, ele sempre substituirá o valor anterior:

// Adicionar um escritor diferente
map.set('autor', 'Outra pessoa!');

Assim, o resultado seria:

Map(3) 
{"nome" => "freeCodeCamp", "tipo" => "blog", "autor" => "Outra pessoa!"}

Como obter valores de um map em JavaScript

Para obter um valor de um map, use o método get(chave):

map.get('nome'); // retorna freeCodeCamp

Tudo sobre chaves nos maps em JavaScript

As chaves em um map podem ser de qualquer tipo, um primitivo ou um objeto. Essa é uma das maiores diferenças entre o map e os objetos regulares em JavaScript, onde a chave só pode ser uma string:

// cria um Map
const funMap = new Map();

funMap.set(360, 'Número da minha casa'); // número como chave
funMap.set(true, 'Eu escrevo blogs!'); // booleano como chave

let obj = {'nome': 'tapas'}
funMap.set(obj, true); // objeto como chave

console.log(funMap);

Aqui está o resultado:

Map(3) 
{
  360 => "Número da minha casa", 
  true => "Eu escrevo blogs!", 
  {…} => true
}

Um objeto regular do JavaScript sempre trata a chave como uma string. Mesmo quando você a passa como um primitivo ou objeto, ela converte internamente a chave em uma string:

// Cria um objeto vazio
const funObj = {};

// Adiciona uma propriedade. Nota, passando a chave como um número.
funObj[360] = 'My House Number';

// Retorna true porque o número 360 foi convertido na string '360' internamente!
console.log(funObj[360] === funObj['360']);

Propriedades de map e métodos em JavaScript

O map em JavaScript tem propriedades e métodos incorporados que o tornam fácil de usar. Aqui estão alguns dos métodos mais comuns:

  • Use a propriedade size para saber quantos elementos estão em um map:
  • Busque um elemento com o método has(chave):
  • Remova um elemento com o método delete(chave):
  • Use o método clear() para remover todos os elementos do map de uma só vez:
console.log('O tamanho do map é', map.size);
// retorna true se o mapa tem um elemento com a chave "John".
console.log(map.has('John')); 


// retorna false se o map não tiver um elemento com a chave "Tapas".
console.log(map.has('Tapas')); 
map.delete('Sam'); // remove o elemento com chave "Sam".
// limpa o map, removendo todos os elementos
map.clear(); 

map.size // retornará, 0

O mapIterator: keys(), values() e entries() em JavaScript

Os métodos keys(), values() e entries() retornam um MapIterator, o que é excelente, pois você pode usar um laço for-of ou forEach diretamente sobre ele.

Primeiro, crie um mapsimples:

const ageMap = new Map([
  ['Jack', 20],
  ['Alan', 34],
  ['Bill', 10],
  ['Sam', 9]
]);
  • Obtenha todas as chaves:
  • Obtenha todos os valores:
  • Obtenha todas as entradas (pares chave-valor):
console.log(ageMap.keys());

// Resultado:

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

// Resultado:

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

// Resultado

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

Como iterar sobre um map em JavaScript

Você pode usar os laços forEach ou for-of para iterar sobre um map:

// com forEach
ageMap.forEach((value, key) => {
   console.log(`${key} tem ${value} anos de idade!`);
});

// com for-of
for(const [key, value] of ageMap) {
  console.log(`${key} tem ${value} anos de idade!`);
}

O resultado será o mesmo em ambos os casos:

Jack tem 20 anos de idade!
Alan tem 34 anos de idade!
Bill tem 10 anos de idade
Sam tem 9 anos de idade!

Como converter um objeto em um map em JavaScript

Você pode encontrar uma situação na qual você precisa converter um objeto em uma estrutura semelhante a um map. Você pode usar o método de objeto entries para fazer isso:

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

const addressMap = new Map(Object.entries(address));

Como converter um map em um objeto em JavaScript

Se você quiser fazer o contrário, você pode usar o método fromEntries:

Object.fromEntries(map)

Como converter um map em um array em JavaScript

Há algumas maneiras de converter um map em um array:

  • Usando Array.from(map):
  • Usando o operador spread:
const map = new Map();
map.set('leite', 200);
map.set("chá", 300);
map.set('café', 500);

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

Map x objeto: quando você deve usá-los?

O map tem características tanto do objeto quanto do array. Entretanto, o map é mais como um objeto do que um array devido à natureza do armazenamento dos dados no formato chave-valor.

A similaridade com os objetos termina aqui. Como você já viu, o map é diferente em muitos aspectos. Então, qual você deve usar e quando? Como você decide?

Use map quando:

  • As suas necessidades não são tão simples assim. Você pode querer criar chaves que não sejam strings. Armazenar um objeto como uma chave é uma abordagem muito poderosa. O map dá a você esse poder por padrão.
  • Você precisa de uma estrutura de dados onde os elementos possam ser solicitados. Os objetos regulares não mantêm a ordem de suas entradas.
  • Você está procurando flexibilidade sem depender de uma biblioteca externa como o lodash. Você pode acabar usando uma biblioteca como o lodash porque não encontrou métodos como has(), values(), delete() ou uma propriedade size com um objeto normal. O map facilita isso para você, fornecendo todos esses métodos por padrão.

Use um objeto quando:

  • Você não tem nenhuma das necessidades listadas acima.
  • Você depende de JSON.parse(), pois um map não pode ser analisado por ele.

Set em JavaScript

Um set é uma coleção de elementos únicos que podem ser de qualquer tipo. O set é também uma coleção ordenada de elementos, o que significa que os elementos serão recuperados na mesma ordem em que foram inseridos.

Um set em JavaScript se comporta do mesmo modo que um conjunto matemático.

Como criar e inicializar um Set em JavaScript

Um novo set pode ser criado assim:

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

O resultado será um set vazio:

Set(0) {}

Veja aqui como criar um set com alguns valores iniciais:

const fruitSet = new Set(['🍉', '🍎', '🍈', '🍏']);
console.log(fruitSet);

Resultado:

Set(4) {"🍉", "🍎", "🍈", "🍏"}

Propriedades e métodos dos sets em JavaScript

O set tem métodos para adicionar um elemento a ele, excluir elementos dele, verificar se existe um elemento nele e limpá-lo completamente:

  • Use a propriedade size para saber o tamanho do set. Ele retorna o número de elementos contidos nele:
  • Use o método add(elemento) para adicionar um elemento ao set:
set.size
// Cria um set - saladSet
const saladSet = new Set();

// Adiciona alguns vegetais a ele
saladSet.add('🍅'); // tomate
saladSet.add('🥑'); // abacate
saladSet.add('🥕'); // cenoura
saladSet.add('🥒'); // pepino

console.log(saladSet);


// Resultado

// Set(4) {"🍅", "🥑", "🥕", "🥒"}

Eu amo tomates! Que tal adicionar mais um?

Oh, não! Eu não posso – o set é uma coleção de elementos únicos:

saladSet.add('🍅');
console.log(saladSet);

O resultado é o mesmo de antes – nada foi adicionado ao saladSet.

  • Use o método has(elemento) para procurar se temos uma cenoura (🥕) ou um brócolis (🥦) no set:
  • Use o método delete(elemento) para remover o abacate(🥑) do set:
// A salada tem uma 🥕, então retorna true
console.log('A salada tem uma cenoura?', saladSet.has('🥕'));

// A salada não tem um 🥦, então retorna false
console.log('A salada tem brócolis?', saladSet.has('🥦'));
saladSet.delete('🥑');
console.log('Eu não gosto de 🥑, remova da salada:', saladSet);

Agora, nosso set de saladas é o seguinte:

Set(3) {"🍅", "🥕", "🥒"}
  • Use o método clear() para remover todos os elementos do set:
saladSet.clear();

Como iterar sobre um set em JavaScript

O set tem um método chamado values() que retorna um SetIterator para obter todos os seus valores:

// Cria um Set
const houseNos = new Set([360, 567, 101]);

// Obtenha o SetIterator utilizando o método values()
console.log(houseNos.values());

Resultado:

SetIterator {360, 567, 101}

Podemos usar um laço forEach ou for-of sobre isso para recuperar os valores.

Curiosamente, o JavaScript tenta tornar o set compatível com o map. É por isso que encontramos nele dois métodos que vimos em map, keys() e entries().

Como set não tem chaves, o método SetIterator retorna um SetIterator para recuperar os seus valores:

console.log(houseNos.keys());

// Resultado

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

Com map, o método entries() retorna um iterador para recuperar pares chave-valor. Novamente, não há chaves em um set, então entries() retorna um SetIterator para recuperar pares valor-valor:

console.log(houseNos.entries());

// Resultado

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

Como enumerar sobre um set em JavaScript

Podemos enumerar sobre um set usando os laços forEach e for-of:

// com forEach

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


// com for-of

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

O resultado de ambos é:

360
567
101

Sets e arrays em JavaScript

Um array, como um set, permite adicionar e remover elementos. O set, no entanto, é bem diferente e não se destina a substituir os arrays.

A maior diferença entre um array e um set é que os arrays permitem que você tenha elementos duplicados. Além disso, algumas das operações de set, como delete() são mais rápidas do que as do array, como shift() ou splice().

Pense em set como uma extensão de um array regular, apenas com mais capacidades. A estrutura de dados do set não é uma substituição do array. Ambos podem resolver problemas interessantes.

Como converter um set em um array em JavaScript

A conversão de um set em um array é simples:

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

Valores únicos de um array utilizando o set em JavaScript

Criar um set é uma maneira realmente fácil de remover valores duplicados de um array:

// Cria um array mixedFruit com algumas frutas duplicadas
const mixedFruit = ['🍉', '🍎', '🍉', '🍈', '🍏', '🍎', '🍈'];

// Passa o array para criar um set de frutas únicas
const mixedFruitSet = new Set(mixedFruit);

console.log(mixedFruitSet);

Resultado:

Set(4) {"🍉", "🍎", "🍈", "🍏"}

Set e objetos em JavaScript

Um set pode ter elementos de qualquer tipo, até mesmo objetos:

// Cria um objeto pessoa
const person = {
   'name': 'Alex',
   'age': 32
 };

// Cria um set e adiciona o objeto a ele
const pSet = new Set();
pSet.add(person);
console.log(pSet);

Resultado:

image-113

Não há surpresas aqui - o set contém um elemento que é um objeto.

Vamos mudar uma propriedade do objeto e adicioná-la ao set novamente:

// Muda o nome da pessoa
person.name = 'Bob';

// Adiciona o objeto pessoa ao set novamente
pSet.add(person);
console.log(pSet);

Qual você acha que será a saída? Dois objetos pessoa ou apenas um?

Aqui está a saída:

image-114

O set é uma coleção de elementos únicos. Ao mudar a propriedade do objeto, não mudamos o objeto em si. Portanto, o set não permitirá a duplicação de elementos.

O set é uma ótima estrutura de dados a ser usada para além dos arrays em JavaScript. No entanto, ele não tem uma grande vantagem sobre os arrays regulares.

Use set quando precisar manter um conjunto distinto de dados para realizar operações de conjuntos, como união, interseção, diferença e assim por diante.

Em resumo

Aqui está um repositório no GitHub onde você pode encontrar todo o código-fonte usado neste artigo. Se você o achou útil, mostre o seu apoio dando uma estrela ao repositório: https://github.com/atapas/js-collections-map-set

Você também pode gostar de alguns dos meus outros artigos (em inglês):

Se este artigo foi útil, compartilhe-o para que outros também possam ler. Você pode entrar em contato com autor pelo Twitter (@tapasadhikary), fazer seus comentários, ou segui-lo por lá.