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.

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

SurveyJS custom survey software

JavaScript Form Builder UI Component

Generate dynamic JSON-driven forms directly in your JavaScript app (Angular, React, Vue.js, jQuery) with a fully customizable drag-and-drop form builder. Easily integrate with any backend system and retain full ownership over your data, with no user or form submission limits.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay