Maps in ES6

Common things between Maps and Sets

  1. Both share common properties and methods
  2. Both are iterable which means you can loop over them.
  3. WeakMaps and WeakSets don't prevent object from being garbage collected.

Maps

Maps are unique because they are collection of key-value pairs whereas Sets are collection of unique values. You can say Sets are similar to Arrays and Maps are similar to objects because Maps store key-value pairs similar to how objects contain named properties with values.

Essentially, a Map is an object that lets you store key-value pairs where both the keys and the values can be objects, primitive values, or a combination of the two.

How to Create a Map

To create a Map, simply type:

const employees = new Map();
console.log(employees);
Map {}

This creates an empty Map employee with no key-value pairs.

Modifying Maps Unlike Sets, you can’t create Maps from a list of values; instead, you add key-values by using the Map’s .set() method.

const employees = new Map();

employees.set('divya.rastogi@gmail.com', { 
    firstName: 'Divya',
    lastName: 'Rastogi',
    role: 'Content Developer' 
});
employees.set('divyanshi.rastogi@gmail.com', {
       firstName: 'Divyanshi',
    lastName: 'Rastogi',
    role: 'Content Developer'
});
employees.set('akshat.raj@gmail.com', {
    firstName: 'Akshat',
    lastName: 'Raj',
    role: 'Content Developer'
});

console.log(employees);
Map {'divya.rastogi@gmail.com' => Object {...}, 'divyanshi.rastogi@gmail.com' => Object {...}, 'akshat.raj@gmail.com' => Object {...}}

The .set() method takes two arguments. The first argument is the key, which is used to reference the second argument, the value.

To remove key-value pairs, simply use the .delete() method.

employees.delete('divyanshi.rastogi@gmail.com');
employees.delete('akshat.raj@gmail.com');
console.log(employees);
Map {'divya.rastogi@gmail.com' => Object {firstName: 'Divya', lastName: 'Rastogi', role: 'Course Developer'}}

Again, similar to Sets, you can use the .clear() method to remove all key-value pairs from the Map.

employees.clear()
console.log(employees);
Map {}

TIP: If you .set() a key-value pair to a Map that already uses the same key, you won’t receive an error, but the key-value pair will overwrite what currently exists in the Map. Also, if you try to .delete() a key-value that is not in a Map, you won’t receive an error, and the Map will remain unchanged.

The .delete() method returns true if a key-value pair is successfully deleted from the Map object, and false if unsuccessful. The return value of .set() is the Map object itself if successful.

Working with Maps

After you’ve built your Map, you can use the .has() method to check if a key-value pair exists in your Map by passing it a key.

const members = new Map();

members.set('Evelyn', 75.68);
members.set('Liam', 20.16);
members.set('Sophia', 0);
members.set('Marcus', 10.25);

console.log(members.has('Xavier'));
console.log(members.has('Marcus'));
false
true

And you can also retrieve values from a Map, by passing a key to the .get() method.

console.log(members.get('Evelyn'));
75.68

Looping Through Maps

You’ve created a Map, added some key-value pairs, and now you want to loop through your Map. Thankfully, you’ve got three different options to choose from:

  1. Step through each key or value using the Map’s default iterator
  2. Loop through each key-value pair using the new for...of loop
  3. Loop through each key-value pair using the Map’s .forEach() method

1. Using the MapIterator

Using both the .keys() and .values() methods on a Map will return a new iterator object called MapIterator. You can store that iterator object in a new variable and use .next() to loop through each key or value. Depending on which method you use, will determine if your iterator has access to the Map’s keys or the Map’s values.

let iteratorObjForKeys = members.keys();
iteratorObjForKeys.next();
Object {value: 'Evelyn', done: false}

Use .next() to the get the next key value.

iteratorObjForKeys.next();
Object {value: 'Liam', done: false}

And so on.

iteratorObjForKeys.next();
Object {value: 'Sophia', done: false}

On the flipside, use the .values() method to access the Map’s values, and then repeat the same process.

let iteratorObjForValues = members.values();
iteratorObjForValues.next();
Object {value: 75.68, done: false}

2. Using a for...of Loop

Your second option for looping through a Map is with a for...of loop.

for (const member of members) {
  console.log(member);
}
 ['Evelyn', 75.68]
 ['Liam', 20.16]
 ['Sophia', 0]
 ['Marcus', 10.25]

However, when you use a for...of loop with a Map, you don’t exactly get back a key or a value. Instead, the key-value pair is split up into an array where the first element is the key and the second element is the value.

3. Using a forEach Loop

Your last option for looping through a Map is with the .forEach() method.

members.forEach((value, key) => console.log(key, value));
 'Evelyn' 75.68
 'Liam' 20.16
 'Sophia' 0
 'Marcus' 10.25

Notice how with the help of an arrow function, the forEach loop reads fairly straightforward. For each value and key in members, log the value and key to the console.

What is a WeakMap?

A WeakMap is just like a normal Map with a few key differences:

  1. a WeakMap can only contain objects as keys,
  2. a WeakMap is not iterable which means it can’t be looped and a WeakMap does not have a .clear() method.
  3. You can create a WeakMap just like you would a normal Map, except that you use the WeakMap constructor.
const book1 = { title: 'Pride and Prejudice', author: 'Jane Austen' };
const book2 = { title: 'The Catcher in the Rye', author: 'J.D. Salinger' };
const book3 = { title: 'Gulliver’s Travels', author: 'Jonathan Swift' };

const library = new WeakMap();
library.set(book1, true);
library.set(book2, false);
library.set(book3, true);

console.log(library);
WeakMap {Object {title: 'Pride and Prejudice', author: 'Jane Austen'} => true, Object {title: 'The Catcher in the Rye', author: 'J.D. Salinger'} => false, Object {title: 'Gulliver’s Travels', author: 'Jonathan Swift'} => true}

…but if you try to add something other than an object as a key, you’ll get an error!

library.set('The Grapes of Wrath', false);
Uncaught TypeError: Invalid value used as weak map key(…)

This is expected behavior because WeakMap can only contain objects as keys. Again, similar to WeakSets, WeakMaps leverage garbage collection for easier use and maintainability.

Garbage Collection In JavaScript, memory is allocated when new values are created and is "automatically" freed up when those values are no longer needed. This process of freeing up memory after it is no longer needed is what is known as garbage collection. WeakMaps take advantage of this by exclusively working with objects as keys. If you set an object to null, then you’re essentially deleting the object. And when JavaScript’s garbage collector runs, the memory that object previously occupied will be freed up to be used later in your program.

book1 = null;
console.log(library);
WeakMap {Object {title: 'The Catcher in the Rye', author: 'J.D. Salinger'} => false, Object {title: 'Gulliver’s Travels', author: 'Jonathan Swift'} => true}