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.

Top comments (0)