DEV Community

Cover image for Javascript: Map e Set
Ivan Trindade
Ivan Trindade

Posted on

Javascript: Map e Set

Sabemos que os objetos, são usados para armazenar coleções codificadas e arrays são usados para armazenar coleções ordenadas. Mas isso não é suficiente para a vida real. É por isso que aprenderemos sobre Map e Set.

Map

Map é uma coleção de itens de dados com chave, assim como um arquivo Object. Mas a principal diferença, é que Map permite chaves de qualquer tipo.

Métodos e propeidades são:

  • new Map() - cria o map.
  • map.set(key, value) - armazena o valor pela chave.
  • map.get(key) - retorna o valor pela chave e undefined, caso a key não exista no map.
  • map.has(key) - retorna true se a key existir, false caso contrário.
  • map.delete(key) - remove o elemento (o par de chave/valor) pela chave.
  • map.clear() - remove tudo do map.
  • map.size - retorna a contagem do elemento atual.

Por exemplo:

let map = new Map()

`map.set('1', 'str1')` // uma key string
`map.set(1, 'num1')` // uma key numérica
`map.set(true, 'bool1')` // uma key booleana

// lembra do objeto regular? converteria as chaves em string
// O map mantém o tipo, então esses dois são diferentes:

alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

Enter fullscreen mode Exit fullscreen mode

Como podemos ver, ao contrário dos objetos, as chaves não são convertidas em strings. Qualquer tipo de chave é possível.

map[key] não é a maneira certa de usar um Map

Embora map[key] também funcione, por exemplo, podemos definir map[key] = 2, isso é tratar o map como um objeto JavaScript simples, portanto, implica todas as limitações correspondentes (apenas chaves de strings/símbolo e assim por diante).

O map também pode usar objetos como chaves

Por exemplo:

let john = { name: "John" };

// para cada usuário, vamos armazenar a contagem de visitas
let visitsCountMap = new Map();

// john é a chave para o map
visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123
Enter fullscreen mode Exit fullscreen mode

Usar objetos como chaves, é um dos recursos mais notáveis e importantes do Map. O mesmo não conta para Object. String como uma chave em Object está correto, mas não podemos usar outro Object como uma chave em Object.

Vamos tentar:

let john = { name: "John" };
let ben = { name: "Ben" };

let visitsCountObj = {}; // tente usar um objeto

visitsCountObj[ben] = 234; // tente usar o objeto ben como a chave
visitsCountObj[john] = 123; // tente usar o objeto john como a chave, o objeto ben será substituído

// Isso é o que está retornando!
alert( visitsCountObj["[object Object]"] ); // 123
Enter fullscreen mode Exit fullscreen mode

Como visitsCountObj é um objeto, ele converte todas as chaves Object, como john e ben acima, na mesma string "[object Object]". Definitivamente não é o que queremos.

Iteração sobre o map

Para fazer um loop sobre um map, existem 3 métodos:

  • map.keys() - retorna um iterável para chaves,
  • map.values() - retorna um iterável para valores,
  • map.entries() - retorna um iterável para [keys, value], é usado por padrão em for...of.

Por exemplo:

let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

// iterar sobre chaves (vegetais)
for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

// iterar sobre valores (quantidades)
for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

// iterar sobre valores [key, value] 
for (let entry of recipeMap) { // o mesmo de recipeMap.entries()
  alert(entry); // pepino, 500 (e assim por diante)
}
Enter fullscreen mode Exit fullscreen mode

A ordem de iserção é usada

A iteração ocorre na mesma ordem em que os valores foram inseridos. Map preserva essa ordem, ao contrário de um Object.

Além disso, Map possui um método forEach embutido, semelhante a um Array:

// executa a função para cada par (key, value)
recipeMap.forEach( (value, key, map) => {
  alert(`${key}: ${value}`); // pepino: 500 etc
});
Enter fullscreen mode Exit fullscreen mode

Object.entries: Map from Object

Quando um Map é criado, podemos passar um array (ou outro iterável) com pares chave/valor para inicialização, assim:

// array of [key, value] pairs
let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

alert( map.get('1') ); // str1
Enter fullscreen mode Exit fullscreen mode

Se tivermos um objeto simples e quisermos criar um Map a partir dele, podemos usar o método integrado Object.entries(obj) que retorna uma array de pares de chave/valor para um objeto exatamente nesse formato.

Assim, podemos criar um map a partir de um objeto como esse:

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John
Enter fullscreen mode Exit fullscreen mode

Aqui, Object.entries retorna o array de pares chave/valor: [ ["name","John"], ["age", 30] ]. Isso é o que Mapprecisa.

Object.fromEntries: Object from Map

Acabamos de ver como criar Map a partir de um objeto simples com Object.entries(obj). Existe o método Object.fromEntries que faz o contrário: dado um array de pares [chave, valor], ele cria um objeto a partir deles:

let prices = Object.fromEntries([
  ['banana', 1],
  ['orange', 2],
  ['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2
Enter fullscreen mode Exit fullscreen mode

Podemos usar Object.fromEntries para obter um objeto simples de Map. Por exemplo, armazenamos os dados em um map, mas precisamos passá-los para um código de terceiros que espera um objeto simples. Aqui vamos nós:

let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // make a plain object (*)

// done!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2
Enter fullscreen mode Exit fullscreen mode

Uma chamada para map.entries() retorna um iterável de pares chave/valor, exatamente no formato correto para Object.fromEntries. Também poderíamos tornar a linha (*) mais curta:

let obj = Object.fromEntries(map); // omit .entries()
Enter fullscreen mode Exit fullscreen mode

Isso é o mesmo, porque Object.fromEntries espera um objeto iterável como argumento. Não necessariamente uma array. E a iteração padrão para map, retorna os mesmos pares chave/valor de map.entries(). Assim, obtemos um objeto simples com os mesmos valores/chave do mapa.

Set

Uma Set é uma coleção de tipo especial – “conjunto de valores” (sem chaves), onde cada valor pode ocorrer apenas uma vez.

Seus principais métodos são:

  • new Set([iterable]) – cria o conjunto e, se um objeto iterable for fornecido (geralmente um array), copia os valores dele para o conjunto.
  • set.add(value) – adiciona um valor, retorna o próprio conjunto.
  • set.delete(value) – remove o valor, retorna true se value existir no momento da chamada, caso contrário false.
  • set.has(value) – retorna true se o valor existir no conjunto, caso contrário false.
  • set.clear() – remove tudo do conjunto.
  • set.size – é a contagem de elementos.

A principal característica é que chamadas repetidas de set.add(value) com o mesmo valor não fazem nada. É por isso que cada valor Set aparece apenas uma vez.

Por exemplo, temos visitantes chegando e gostaríamos de lembrar de todos. Mas visitas repetidas não devem levar a duplicatas. Um visitante deve ser “contado” apenas uma vez. Por exemplo;

let set = new Set();

let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };

// visitas, alguns usuários vêm várias vezes
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// set mantém apenas valores únicos
alert( set.size ); // 3

for (let user of set) {
  alert(user.name); // John (então Pete e Mary)
}
Enter fullscreen mode Exit fullscreen mode

Uma alternativa a Set pode ser uma array de usuários e o código para verificar se há duplicatas em cada inserção usando arr.find. Mas o desempenho seria muito pior, porque esse método percorre todo o array verificando cada elemento. Set é muito melhor otimizado internamente para verificações de exclusividade.

Iteração sobre Conjunto

Podemos percorrer um conjunto com for..of ou usando forEach:

let set = new Set(["laranjas", "maçãs", "bananas"]);

for (let value of set) alert(value);

// o mesmo com forEach:
set.forEach((value, valueAgain, set) => {
  alert(value);
});
Enter fullscreen mode Exit fullscreen mode

Observe uma coisa engraçada. A função de retorno de chamada de forEach possui 3 argumentos: um value, o mesmo valor valueAgain e, em seguida, o objeto de destino. De fato, o mesmo valor aparece nos argumentos duas vezes.

Isso é para compatibilidade com Map onde o callback passado forEach tem três argumentos. Parece um pouco estranho, com certeza. Mas isso pode ajudar a substituir Map com Set com facilidade em certos casos e vice-versa.

Os mesmos métodos Mappara iteradores também são suportados:

  • set.keys() – retorna um objeto iterável para valores,
  • set.values() – o mesmo que set.keys(), para compatibilidade com Map,
  • set.entries() – retorna um objeto iterável para entradas [value, value], existe para compatibilidade com Map.

Resumo

Map – é uma coleção de valores chaveados.

Métodos e propriedades:

  • new Map([iterable]) – cria o map, com opcional iterable(por exemplo, array) de pares [key,value] para inicialização.
  • map.set(key, value) – armazena o valor pela chave, retorna o próprio map.
  • map.get(key) – retorna o valor pela chave, undefined caso key não exista no map.
  • map.has(key) – retorna true se a key existir, false caso contrário.
  • map.delete(key) – remove o elemento pela chave, retorna true se key existe no momento da chamada, caso contrário false.
  • map.clear() – remove tudo do map. -map.size – retorna a contagem do elemento atual.

As diferenças de um regular Object:

Quaisquer chaves, objetos podem ser chaves.
Métodos convenientes adicionais, uma propriedade size.
Set – é uma coleção de valores únicos.

Métodos e propriedades:

  • new Set([iterable]) – cria o conjunto, com valores opcionais iterable(por exemplo, array) para inicialização.
  • set.add(value) – adiciona um valor (não faz nada se value existir), retorna o próprio conjunto. set.delete(value) – remove o valor, retorna true se value existir no momento da chamada, caso contrário false.
  • set.has(value) – retorna true se o valor existir no conjunto, caso contrário false.
  • set.clear() – remove tudo do conjunto.
  • set.size – é a contagem de elementos.

Latest comments (0)