Map & WeakMap in JavaScript

Pavlo Savchuk
6 min readNov 10, 2020
Photo by Christina Morillo: Pexels

Hi guys, how have you been? Today I’ll describe 2 data structures in JavaScript such as Map and WeakMap. I’ll describe what are they for and how I apply them. Let’s get started.

Map — is a collection of key/value pairs, just like an Object. The key difference is Map stores keys of any type (whereas Object stores keys as a String type).

Map methods and properties:

  • new Map([iterable]) – creates a new collection. The Map() receive an optional iterable object whose elements are key-value pairs.
  • map.set(key, value) – sets a value by the given key. The key may be of any type. This is important and will be demonstrated in the examples below.
  • map.get(key) – returns the value by the specified keyor undefined if the correspondent value is not present in the map.
  • map.has(key) – returns true if thekey is in the collection, and false otherwise.
  • map.delete(key) – deletes the element by the given key. Returns true if the element was deleted and false if the element was not in the collection.
  • map.clear() – cleans up the whole collection.
  • map.size – returns the number of elements in the collection.
  • forEach(callback[, thisArg]) – invokes a callback for each key-value pair in the map in the insertion order. The optional thisArg parameter sets the this value for each callback.

To loop through Map you have 3 methods to access data:

  • map.keys() – returns an iterable object of Map keys.
  • map.values() – returns an iterable object of Map values.
  • map.entries() – returns an iterable object of [key, value] pairs. That would be the default for using in for..of the loop.

Why and when I use Map?

  1. Performance. That’s the killer to me. Especially when dealing with the high loaded applications. The thing is how the map is saved in memory and when you need to access a specific item, it directly gives you the value stored in memory corresponded to that key.
  2. You can use an object as a key. E.g. whenever I need to save additional data to the user object, that does not need to be a part of a user object, I use Map. Whenever I need to save some meta-data for the entity — I use Map. In the past, I used to add a needed meta-data to the user object.

Examples

Let’s consider some samples:

const map = new Map();map.set("0", "Lorem impsum"); // "0" as a key of a type 
"string"
map.set(0, "Hey!") // 0 as key with a type "number"map.set(true, "Example of having a boolean value as a key"); //boolean as a key.map.get(0) // "Hey!"
map.get("0") // "Lorem impsum"
/*Map stores key types so we have 2 separate values. If we had an object to save these values, object would convert keys into strings and we would have the same value for both "0" and 0 keys.*/
map.get(true) // "Example of having a boolean value as a key"/*Map can use objects as the keys*/
const user = { name: "Pavlo", .... };
/*Let's say we need to know the number of pages the user visited in our app. */const numberOfPagesMap = new Map();
numberOfPagesMap.set(user, 12);
numberOfPagesMap.get(user); // 12

If we try to switch Map to Object in the example above we would get the following:

const user = { name: "Pavlo", .... };/*Let's say we need to know the number of pages the user visited in our app.*/const numberOfPagesMap = {}; //declaring object  
numberOfPagesMap[user] = 12;
/*This is how JavaScript saved it:
numberOfPagesMap["[object Object]"] = 12;*/

Indeed, since user is an object in the example above, JavaScript converts key value to a string type and then saves it.

We also can chain set()method calls, since set()returns Map. Like below:

map.set("1", true).set(2,false).set({name:"John"}, {flag:true})

Map Looping

Note: looping through the Map happens in the same order in which elements were set in the Map. It is different from looping through Object, where keys are in sorted order.

/*Let's assume we are given a collection of employees in your town*/const companyEmployeesMap = new Map([
["Samsung", 800 ],
["Panasonic", 120],
["Sony", 440],
["LG", 350],
]);
// loop through keys
for (let company of companyEmployeesMap.keys()) {
console.log(company); // Samsung, Panasonic, Sony, LG
}
// loop through values
for (let company of companyEmployeesMap.values()) {
console.log(company); // 800, 120, 440, 350
}
// loop through elements via [key,value] pair
for ( const item of companyEmployeesMap){
console.log(item) // ["Samsung", 800 ] ...etc
} //that's the same as companyEmployeesMap.entries()
// loop through elements with forEach
companyEmployeesMap.forEach((value, key, map) => {
console.log(`${key}: ${value}`); // Samsung: 800 .....
});

typeof & instanceof Map

Let’s say we have a contst userMetaDataMap = new Map(). This userMetaDataMap is an instance of the Map object.

typeof(userRoles);  // "object" 
userRoles instanceof Map; // true

Create a Map from an Object

Above, we have an example when we created a Map from the array of key-value pairs — companyEmployeesMap. If we intend to create a Map from the Object we may use Object.etnries a method like the below:

const companyEmployees ={
"Samsung": 800 ,
"Panasonic": 120,
"Sony": 440
};
const userMetaDataMap = new Map(Object.entries(companyEmployees));

The example above Object.entries() returns an array of key-value pairs that we use to create a new Map.

Create an Object from a Map

In case we want to create an Object from a Map we need to apply Object.fromEntries in conjunction with map.entries like below

const map = new Map([
["Samsung", 800 ],
["Panasonic", 120],
["Sony", 440],
["LG", 350],
]);
const companyEmployeesObject = Object.fromEntries(map.entries())

WeakMap

WeakMap — is a collection of key/value pairs in which the keys are weakly referenced. The keys must be objects and the values can be arbitrary values.

The main differences from theMap are:

  1. In WeakMap key must be only Objects. You will get an error “Iterator value is not an entry object” otherwise.
  2. In WeakMap, as soon as the key object is removed from memory, it will also be removed from WeakMap. The objects in JavaScript get removed from the memory when there is no reference to them.
  3. The methods are limited to:get(key), set(key, value), has(key), delete(key)
  4. Limitations: items cannot be iterated, you can’t check the size of the collection, you cannot clear all elements from the collection.

Consider the following example:

let page = { title: "Home page" }; 
const weakMap = new WeakMap();
weakMap.set(page, 75);
page = null; // object is removed from the memory

When I use WeakMap?

Why would you ever use WeakMap? For caching it is perfect.

Consider the caching function example below.
Let’s say we have objects we want to process and save the processing result in cache storage.

const cacheStorage = new WeakMap();const processObjectData = obj => { 
if (!cacheStorage.has(obj)) {
const dataToSaveInCache = "....some calculated data...";
cacheStorage.set(obj, dataToSaveInCache);
}
return cacheStorage.get(obj);
}
const homePageObject = {
...some page related data
};
const userAccountPageObject = {
...some page related data
};
let homePage = processObjectData(homePageObject);
let accountPage = processObjectData(userAccountPageObject);
//when we do not need account page we can delete the data by doing
accountPage = null;
//the line above will also remove the data from the cacheStorage automatically since the object reference does not exist.

The huge advantage of the WeakMap is that you do not have to think of memory releasing it is done automatically by a Garbage Collector.

If the example above would use Map instead of WeakMap, after assigning the object to null, it still remains in the Map collection.

Conclusion:

  • The keys of an Object must be either a String or a Symbol. A Map's keys can be any value, including functions, objects, or any primitive.
  • The keys of a Map are ordered in a simple, straightforward way: A Map object iterates entries, keys, and values in the order of entry insertion. In the Objects, the keys are sorted in defined sort order.
  • The number of items in a Map are retrieved from the size property. The number of items in an Object must be determined manually.
  • Map performs better in scenarios involving frequent additions and removals of key-value pairs. Objects are not optimized for frequent additions and removals of key-value pairs.

Use Map and WeakMap when you need to cache or store metadata for the object. It will allow you to keep the initial object clean, without the data you do not need, and also will boost your app performance.

In addition to this, Map provides you with the ability to build a complex structure for the objects. It also allows you to access a needed key in a fast and efficient way. On top of that, you can easily check if the element is present in the collections usinghas() instead of filtering an array for a needle element.

Hopefully, you found this article useful, and this is something that you would apply to your code. In the next article, I’ll write about Set and WeakSet collections.

--

--

Pavlo Savchuk

Professional software developer with a degree in Computer Science and vast development experience. On Medium, I write about JavaScript