Map & Set

As novas estruturas de dados

Em conteúdos anteriores, nós vimos que os objetos são usados para o armazenamento de estruturas de dados complexas, onde estes são separados por chave (key) e valor (value).

Um objeto é criado por meio das chaves {...}, onde dentro delas temos uma estrutura de chave e valor, onde a chave é sempre representado por uma string e o valor podendo ser qualquer outro tipo de dado.

Sob outra perspectiva, nós temos também os arrays, que são considerados um outro tipo de estrutura de dados, que representa uma coleção ordenada capaz de agrupar qualquer dado por um índice.

Diferente dos objetos, os arrays possuem índices numéricos que são criados de forma automática, isto é, sempre quando inserimos um novo tipo de dado dentro daquele array, além disso, os arrays são criados por meio dos colchetes [...].

Até o ECMAScript 2015, objetos e arrays eram as únicas formas de se trabalhar com estruturas de dados, no entanto, até aquela época, os desenvolvedores tiveram que lidar com algumas falhas presentes nesses dois tipos de comandos:

  • As chaves dos objetos só podiam ser do tipo string.
  • Os objetos não mantinham uma ordem quando inseríamos novos elementos dentro deles.
  • Os objetos não continham alguns métodos úteis como acontece nos arrays, e o método length é um exemplo disso.
  • Além disso, arrays permitem duplicatas, fazendo com que o desenvolvedor crie outros artifícios para tentar evita-las (caso necessário).
  • Não era possível criar estruturas de chave e valor dentro dos arrays.

Mas com a chegada do ES6, nossos problemas acabaram, pois a partir de agora temos duas novas estruturas de dados, que são conhecidas como Map e Set.

Elas vieram justamente para preencher aquela lacuna deixada pelos objetos e arrays.

Map

Representando o melhor de dois mundos, o map é uma coleção de pares de chave (key) e valor (value), permitindo que as chaves sejam de qualquer tipo de valor, desde uma string, int, boleano, symbol e afins.

Ao mesmo tempo que é armazenada a ordem em que os elementos foram adicionados, fazendo com que esses itens sejam recuperados na ordem em que eles foram inseridos.

Para criar um map usamos o comando new Map() com a seguinte estrutura:

const meuMap = new Map([
['nome', 'Micilini'],
['Idade', 25]
]);

O código acima irá criar um map com 2 elementos, onde o primeiro argumento (nome/idade) representa a chave, e o segundo argumento (Micilini/25) representa o valor.

Map(2) {'nome' => 'Micilini', 'Idade' => 25}

No caso do código acima, criamos uma chave do tipo string, mas nada impede que criemos uma chave de outro tipo, vejamos:

const meuMap = new Map([
[98, 'Micilini'],
[true, 12],
[88.9, false]
]);

//Map(3) {98 => 'Micilini', true => 12, 88.9 => false}

Criando um Map vazio

Assim como acontece em objetos e arrays, também podemos criar um map vazio, para que mais tarde nós possamos adicionar elementos a ele:

const meuMap = new Map();

console.log(meuMap);//Map(0) {}

Adicionando elementos dentro de um Map

Para adicionarmos novos elementos dentro de um map, usamos o comando set().

Ele toma dois argumentos, que são chave e valor, onde a chave pode ser de qualquer tipo de valor, e até mesmo quem sabe um objeto.

const meuMap = new Map();

meuMap.set('site', 'micilini.com');
meuMap.set('33', true);
meuMap.set(true, 'micilini');

console.log(meuMap);//Map(3) {'site' => 'micilini.com', '33' => true, true => 'micilini'}

Mas o que será que acontece quando definimos uma chave e um novo elemento com uma chave que já existe?

O JS substitui o valor dessa chave:

const meuMap = new Map();

meuMap.set('nome', 'Micilini');
meuMap.set('nome', 'Micilini Roll');

console.log(meuMap);//Map(1) {'nome' => 'Micilini Roll'}

Selecionando valores de um Map

Diferente dos objetos e arrays, em que selecionamos os elementos por meio dos colchetes [] ou do ponto (.), no caso do map, precisamos usar um método para isso, o método get();

const meuMap = new Map([
['site', 'micilini.com'],
[33, true],
[true, 33]
]);

console.log(meuMap.get('site'));//micilini.com
console.log(meuMap.get(33));//true
console.log(meuMap.get(true));//33

Adicionando e selecionando objetos em chaves de um Map

Como a chave de um map pode ser qualquer tipo de valor, você também pode adicionar um objeto como uma chave:

const meuMap = new Map();

meuMap.set(124, 'Olá Mundooo');
meuMap.set(true, 98);

let obj = {'site': 'micilini.com'};
meuMap.set(obj, true);//Aqui estamos criando o objeto dentro do map

console.log(myMap.get(obj));//Aqui ele seleciona o objeto, resultado: true

Propriedades e métodos de um Map

O JS conta com alguns comandos padrão que podemos usar em nosso map, de modo a facilitar a interação com ele, vejamos alguns deles abaixo:

Size

A propriedade size retorna a quantidade de elementos que existem dentro de um map.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

console.log(meuMap.size);//2

Has()

O método has() busca a existência de um elemento com a chave dentro de um map, retornando true se encontrar, e false se não encontrar.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

console.log(meuMap.has('nome'));/true
console.log(meuMap.has(24));/false

Delete()

O método delete() remove um determinado elemento de um map por meio de uma chave especificada.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

meuMap.delete('nome');

Clear()

O método clear() limpa (excluí) todos os elementos de um map de uma só vez.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

meuMap.clear();

Interagindo com o Map

Existem algumas formas de se interagir com o map, e isso é feito por meio de alguns de seus métodos, vejamos cada um deles abaixo.

Lembrando que você pode usar os laços for...in ou for...of para interagir com cada um deles posteriormente.

Keys()

O método keys() retorna um objeto contendo todas as chaves de um map.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

const chaves = meumap.keys();

console.log(chaves);//'nome', true

Values()

O método values() retorna um objeto contendo todos os valores de um map.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

const valores = meumap.values();

console.log(valores);//'Micilini', 12

Entries()

O método entries() retorna um objeto contendo todas as chaves e valores de um map.

const meuMap = new Map([
['nome', 'Micilini'],
[true, 12],
]);

const resultado = meuMap.entries();

console.log(resultado);//{'nome' => 'Micilini', true => 12}

ForEach

Podemos fazer o uso do forEach() para interagir com um map.

const idadesMap = new Map([
['João', 28],
['Gabriel', 19],
['Paulo', 43]
]);

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

For...Of

Podemos também fazer o uso do for...of para interagir com um map.

const idadesMap = new Map([
['João', 28],
['Gabriel', 19],
['Paulo', 43]
]);

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

Convertendo um Objeto em um Map

Com o JS é possível converter um objeto para um map por meio do comando entries(), observe:

const idadesObjeto = {
'Gabriel': 24,
'João': 59,
'Marcia': 44
};

const idadesMap = new Map(Object.entries(idadesObjeto));

Convertendo um Map em um Objeto

Também é possível realizar o processo inverso com o comando Object.fromEntries(map), observe:

const idadesMap = new Map([
['João', 28],
['Gabriel', 19],
['Paulo', 43]
]);

const idadesObjeto = Object.fromEntries(idadesMap);

Convertendo um Map em um Array

Também temos a possibilidade de converter um map em um array, e existem duas formas de se fazer isso.

A primeira delas envolve o uso do método form():

const listaMap = new Map();
listaMap.set('carne', 87);
listaMap.set("frango", 24);
listaMap.set('peixe', 45);

const listaArray = Array.from(map);

console.log(listaArray);

A segunda forma, envolve o uso do operador spread (...):

const listaArray = [...listaMap];

console.log(listaArray);

Fique tranquilo pois no próximo conteúdo iremos falar mais sobre o funcionamento do operador spread 😉

Set

Um Set é uma coleção de elementos únicos, que são ordenadas de acordo com a inserção dos mesmos, podendo estes ser de qualquer tipo.

Para que possamos entende-lo melhor, pense no SET como um conjunto de chaves sem valores.

Criando um Set

Uma coleção de Set pode ser criada desta maneira:

const frutasSet = new Set(['abacaxi', 'pera', 'uva', 'maça']);

console.log(frutasSet);//Set(4) {'abacaxi', 'pera', 'uva', 'maça'}

Caso desejar, podemos também criar um set vazio, para que mais tarde possamos adicionar novos elementos a ele, e isso é feito da seguinte forma:

const meuSet = new Set();

console.log(meuSet);//Set(0) {}

Adicionando elementos a um Set

Para adicionar um novo elemento na sua coleção, você pode fazer o uso do método add():

const meuSet = new Set();

meuSet.add('ola');
meuSet.add(24);
meuSet.add(true);

console.log(meuSet);//Set(3) {'ola', 24, true}

Legal, não acha?

Mas o que será que acontece quando tentamos adicionar um elemento que já existe dentro do set? Como por exemplo:

const meuSet = new Set();

meuSet.add('ola');
meuSet.add('ola');

console.log(meuSet);//Set(1) {'ola'}

O resultado será apenas 1, pois só podemos ter elementos únicos dentro de um set.

Diferenças entre um Set e um Array

Como visto anteriormente, um set é uma coleção de elementos únicos, tanto que não conseguimos adicionar a lista elementos que já foram adicionados.

Já o array é uma lista de elementos onde podem conter repetições, até porque por de baixo dos panos, o JS cria um índice para cada um desses elementos.

Nesse caso podemos dizer que os arrays são considerados como um tipo de estrutura de dados de “coleção de valores que são possuem índices”, enquanto Set é considerado como “coleção de chaves”.

E como na programação não podemos ter chaves ou índices com o mesmo valor, o Set faz jus ao seu nome.

O Set em si, tem vantagens muito claras em relação aos arrays, exceto em cenários específicos, como quando queremos manter dados “distintos” com o mínimo de esforço ou trabalhar com muitos conjuntos de dados distintos de forma agrupada, usando as operações de conjunto mais básicas, sem a necessidade de acesso direto ao elemento.

Caso contrário, Array deve sempre ser a sua escolha principal. Pois da menos trabalho para a CPU quando você precisa recuperar um elemento.

No más, o Set conta com alguns métodos que funcionam de forma mais rápida se comparado com outros métodos de um array, como é o caso dos métodos delete()shift() ou splice().

Agora que já tiramos essa dúvida fatal, vamos continuar aprendendo um pouco mais sobre o Set 😄

Métodos e Propriedades de um Set

Assim como os objetos, arrays e maps, os Sets também contam com uma lista de métodos e propriedades que são oferecidos de forma padrão pelo Javascript.

Vejamos cada um deles a seguir:

Size

A propriedade size é usada para retornar o número de elementos existente em um set.

const meuSet = new Set(['a', 'b', 'c']);

console.log(meuSet.size);//3

Has()

O método has() é usado para verificar se existe um determinado elemento dentro da coleção, se existir é retornado true, caso contrário retorna false.

const meuSet = new Set(['a', 'b', 'c']);

console.log(meuSet.has('a'));//true

console.log(meuSet.has('d'));//false

Delete()

O método delete() como o nome já diz, serve para remover um determinado elemento dentro da coleção.

const meuSet = new Set(['a', 'b', 'c']);

meuSet.delete('b');

console.log(meuSet);//'a'

Clear()

O método clear() serve para limpar os elementos de um set, ou seja, excluí todos os elementos ali existentes.

const meuSet = new Set(['a', 'b', 'c']);

meuSet.clear('b');

console.log(meuSet);//0

Iterando com Set

Como em qualquer coleção de dados, nós podemos criar loops por meio de métodos, e com o Set isso não vai ser diferente.

Values()

O método values() retorna um iterador contendo somente as chaves de modo que possamos interagir sobre todos os seus elementos.

Como o Set é formado só de chaves e não de valores, ele retorna as chaves como se fossem valores.

const meuSet = new Set(['a', 'b', 24]);

const resultado = meuSet.values();

console.log(resultado);//{'a', 'b', 24}

Caso desejar, você pode usar os métodos forEach() e For...Of para interagir com a variável "resultado" declarada acima.

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

for(valor of resultado) {
console.log(valor);
}

Keys()

O método keys() retorna um iterador contendo somente as chaves de modo que possamos interagir sobre seus elementos.

Como o Set é formador só de chaves, ele retorna somente as chaves.

const meuSet = new Set(['a', 'b', 24]);

const resultado = meuSet.keys();

console.log(resultado);//{'a', 'b', 24}

Caso desejar, você pode usar os métodos forEach() e For...Of para interagir com a variável "resultado" declarada acima.

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

for(valor of resultado) {
console.log(valor);
}

Entries()

O método entries() retorna um iterador contendo chave e valor de modo que podemos interagir sobre seus elementos.

Como o Set é formado só de chaves, aqui ele retorna a chave normalmente onde o valor contém o mesmo dado na chave.

const meuSet = new Set(['a', 'b', 24]);

const resultado = meuSet.entries();

console.log(resultado);//{'a' => 'a', 'b' => 'b', 24 => 24}

Podemos usar o ForEach ou o For...Of para interagir:

resultado.forEach((value, key) => {
console.log(`${key} = ${value}`);
});

// com for-of
for(const [key, value] of resultado) {
console.log(`${key} = ${value}`);
}

Convertendo um Set em um Array

Para converter um Set em um array podemos usar o spread(...):

const meuSet = new Set(['a', 'b', 24]);

const arr = [...meuSet];

console.log(arr);

Convertendo um Array em um Set

Para converter um array em um set, podemos instanciar um set levando um array como argumento.

Lembre-se de que os valores repetidos no array serão removidos quando se transformarem em um set.

const meuArray = ['a', 'b', 'a', 'c', 'c'];

const meuSet = new Set(meuArray);

console.log(meuSet);//'a', 'b', 'c'

Conclusão

Neste conteúdo você aprendeu um pouco mais sobre as novas estruturas de coleção de dados que vieram a partir do ES6, que são o Map e o Set.

Até a próxima jornada 🤓