DEV Community

Cover image for JavaScript Map and Set Tutorial: how to use new built-in classes
Amanda Fawcett for Educative

Posted on • Originally published at educative.io

JavaScript Map and Set Tutorial: how to use new built-in classes

In the past, JavaScript was limited when it came to collections. While other languages offer lists, sets, associative maps, and dictionaries, JavaScript only offered only arrays. JavaScript programmers had to come up with workaround for creating sets or maps, which made the code hard to maintain.

Now, with ES6, JavaScript offers new built-in classes for sets and maps that make programming far easier to maintain. In this tutorial, we will introduce you to map and set in detail along with code examples, use cases, and hands-on exercises. This tutorial is ideal for JavaScript developers who want to modernize their JavaScript skills.

This tutorial at a glance:

What is map?

Prior to ES6, JavaScript developers used objects to map keys to values. However, using an object as a map has its limitations. For example:

  • There is no foolproof way to iterate over keys, and the keys() method converts fields to strings, which leads to collision of keys.
  • There is no simple way to add new keys and values

ES6 introduced some new built-in classes, including a collection type called Map, which can hold key-value pairs of any type. Unlike the object approach, the new Map object can remember key insertion order.

In simple terms, a JavaScript Map is an associative collection of distinct keys and values. Both keys and values can be any primitive or object. This is a significant data structure with many valuable uses.

Note: A WeakMap is similar to a Map but all keys of a WeakMap are objects.

To create a new Map, we use the following syntax:


let map = new Map([iterable]);

Enter fullscreen mode Exit fullscreen mode

Let's put this into practice with a more complex example. Below we have a Map that holds names as keys and scores as values.

'use strict';

//START:DEFINE
const scores = 
  new Map([['Sara', 12], ['Bob', 11], ['Jill', 15], ['Bruce', 14]]);

scores.set('Jake', 14);

console.log(scores.size);
//END:DEFINE      
Enter fullscreen mode Exit fullscreen mode

Output: 5

  • The scores Map has been initialized with names and scores. The initial data may be any iterable with a pair of keys and values.
  • We add a key and value to the Map using the set() method (line 7)
  • To figure out how many keys are currently in the Map, we use the size property (line 9)

Note: Map.has(key) above will return the Boolean value to indicate if the element associated with a specified key is in the map

How to use map

Once we know how to create maps with JavaScript, there are a lot of things we can do with them.

Iterate through maps

First, let's learn about iteration through maps. There are 3 methods we can use:

  • map.keys(): returns an iterable for keys
  • map.entries(): returns an iterable for entries [key, value]
  • map.values(): returns an iterable for values

We can iterate over the collection of keys and values with the entries() method, which returns an iterable, so we can use the enhanced for loop along with destructuring.

For example, below, we extract the name and score for each key-value pair:

'use strict';

//START:DEFINE
const scores = 
  new Map([['Sara', 12], ['Bob', 11], ['Jill', 15], ['Bruce', 14]]);

scores.set('Jake', 14);
//END:DEFINE                                                                   

for(const [name, score] of scores.entries()) {
  console.log(`${name} : ${score}`);
}
Enter fullscreen mode Exit fullscreen mode

Output:
Sara : 12
Bob : 11
Jill : 15
Bruce : 14
Jake : 14

We can also use the forEach method, which is an internal iterator.

'use strict';

//START:DEFINE
const scores = 
  new Map([['Sara', 12], ['Bob', 11], ['Jill', 15], ['Bruce', 14]]);

scores.set('Jake', 14);
//END:DEFINE                                                                   

scores.forEach((score, name) => console.log(`${name} : ${score}`));
Enter fullscreen mode Exit fullscreen mode

Output:
1.88s
Sara : 12
Bob : 11
Jill : 15
Bruce : 14
Jake : 14

The first parameter that the function receives is the value for a key that appears as the second parameter. The same forEach() method can be used to iterate over only the values:

'use strict';

//START:DEFINE
const scores = 
  new Map([['Sara', 12], ['Bob', 11], ['Jill', 15], ['Bruce', 14]]);

scores.set('Jake', 14);
//END:DEFINE                                                                   

scores.forEach(score => console.log(score));
Enter fullscreen mode Exit fullscreen mode

Output:
1.85s
12
11
15
14
14

If you receive only one parameter, it will be the value, and if you receive two parameters, then it will stand for the value and key for each key-value pair.

Initialize a map with an iterable object

You can also pass an iterable object to the Map() constructor:

let userRoles = new Map([
    [sarah, 'admin'],
    [bob, 'editor'],
    [jill, 'subscriber']
]);
Enter fullscreen mode Exit fullscreen mode

Get an element from a map by key

We can get an elements from a map by key with the get() method:

But if you pass a key that is not in that map, the it will return undefined.

userRoles.get(sarah); // admin
Enter fullscreen mode Exit fullscreen mode

But if you pass a key that is not in that map, the it will return undefined.

let foo = {name: 'Foo'};
userRoles.get(foo); //undefined
Enter fullscreen mode Exit fullscreen mode

Get the number of elements in the map

We can use the size property to get the number of elements in our maps.

console.log(userRoles.size); // 3
Enter fullscreen mode Exit fullscreen mode

Convert map keys or values to array

At times, you may want to work with an array instead of an iterable object. We can use the spread operator to convert keys for each element into a new array.

var keys = [...userRoles.keys()];
console.log(keys);
Enter fullscreen mode Exit fullscreen mode

This piece of code will convert the values of elements to an array:

var roles = [...userRoles.values()];
console.log(roles);
Enter fullscreen mode Exit fullscreen mode

Other important Map methods

  • clear(): removes elements from the map object.
  • map.set(key, value): stores the value by the key
    • delete(key): removes a specific element (as specified by the key)
  • set(key, value): sets the value for the key and returns the map object. Can be chained with other methods.
    • forEach(callback[, thisArg]): invokes a callback for each key-value pair in insertion order. The thisArg parameter is optional and sets the this value for each callback.
  • has(key): returns true if a value associated with the key exists, otherwise, false.
  • keys(): returns a new iterator with the keys for elements in insertion order.
  • values(): returns a new iterator object with the values for each element in insertion order.
  • map.size: returns the current element count

What is set?

Set is another new collection introduced by ES6. The Array class of JavaScript can work with ordered collection of data, but not so well with unordered collections or when values held in the collection are unique. That's why JavaScript introduced Set.

A set is a unique collection of primitives and objects, and duplicates are not allowed. We can either create an empty set and add objects, or we can initialize a set with the contents of an iterable (like an array).

Let's explore this with an example. Below, we have a set of names with five values. One of the values is not included in the set due to duplication.

'use strict';

//START:CREATE
const names = new Set(['Jack', 'Jill', 'Jake', 'Jack', 'Sara']);
//END:CREATE

//START:SIZE
console.log(names.size);
//END:SIZE
Enter fullscreen mode Exit fullscreen mode

Output: 4

We can add elements to an existing set, like below:

names.add('Matt');
Enter fullscreen mode Exit fullscreen mode

The add() method returns the current Set, which is useful for chain operations, such as more calls to add() or other methods of Set:

names.add('Kate')
  .add('Kara');
Enter fullscreen mode Exit fullscreen mode

How to use set

Once we figure out how to create sets, it's easy to work with them. First, let's look at the built-in functions for sets:

  • has(): to check if a set has a particular element.
  • clear(): to empty an existing set or remove an existing element using the delete() method.
  • keys(): to get all the values from a Set
  • entries(): to iterate over a Set using the enhanced for loop, like below:
'use strict';

//START:CREATE
const names = new Set(['Jack', 'Jill', 'Jake', 'Jack', 'Sara']);
//END:CREATE

//START:ADD
names.add('Mike');
//END:ADD

//START:ADD2
names.add('Kate')
  .add('Kara');
//END:ADD2

console.log(names.has('Brad'));                                         

console.log(names.entries());
console.log(names.keys());
console.log(names.values());

//START:ITERATE1
for(const name of names) {
  console.log(name);
}
//END:ITERATE1
Enter fullscreen mode Exit fullscreen mode

filter/map with sets

Set does not yet offer methods like filter() and map(), but we can create an array from the set and use a functional style methods on that new array.

For example, below we use methods filter(), map(), and forEach() to pick only names that start with J and then transform them to uppercase.

'use strict';

//START:CREATE
const names = new Set(['Jack', 'Jill', 'Jake', 'Jack', 'Sara']);
//END:CREATE

//START:ADD
names.add('Mike');
//END:ADD

//START:ADD2
names.add('Kate')
  .add('Kara');
//END:ADD2


//START:FILTER
[...names].filter(name => name.startsWith('J'))
  .map(name => name.toUpperCase())
  .forEach(name => console.log(name));
//END:FILTER
Enter fullscreen mode Exit fullscreen mode

Output:
JACK
JILL
JAKE

Get the size of a Set

Use the size property of the Set object to return its size.

let size = chars.size;
console.log(size);//  3
Enter fullscreen mode Exit fullscreen mode

Remove elements from a set

To remove an element from a set, use the delete() method.

chars.delete('f');
console.log(chars); // Set {"a", "b", "c", "d", "e"}
Enter fullscreen mode Exit fullscreen mode

And to delete all elements of a set, use the clear() method:

chars.clear();
console.log(chars); // Set{}
Enter fullscreen mode Exit fullscreen mode

Invoke a callback function on each element

To invoke a callback on every element of your set, use the forEach() method.

roles.forEach(role => console.log(role.toUpperCase()));

Enter fullscreen mode Exit fullscreen mode

Other important Set methods

  • new Set(iterable): creates a set.
  • set.add(value): adds a given value and returns the set
  • set.has(value): returns true if a value exists in the set, otherwise, returns false.
  • set.clear(): removes everything from a set

Hands-on exercise with map

To solidify your learning, let's do a hands-on exercise with map in JavaScript. Use Map to get the desired output as given below. When creating an object of createTodo(), it should return a map element.

Input calls Output
console.log(todo.get('learn JavaScript')); done
console.log(todo.get('write elegant code')); work-in-progress
console.log(todo.get('automate tests')); work-in-progress
console.log(completedCount(todo)); 1

The solution to this challenge is given below.

'use strict';

const createTodo = function() {
  const todo = new Map();
  todo.set('learn JavaScript', 'done');
  todo.set('write elegant code', 'work-in-progress');
  todo.set('automate tests', 'work-in-progress');

  return todo;
}; 

const completedCount = function(map) {
  return [...map.values()]
    .filter(value => value === 'done')
    .length;
};

const todo = createTodo(); //Returns a Map
Enter fullscreen mode Exit fullscreen mode

Solution Breakdown

Start by creating a map element. The Map object todo is created on line 4 using the built-in class. You can see that the map object, todo is calling Map.get() with different keys to get their values. This means that we need to add all the keys and values.

We add the new element in todo with the keys and associated values. On lines 5-7, we add the new elements by setting values for the keys.

For completedCount(), we define a new function with a map object parameter. The function will return the count of tasks that are completed. So, essentially, we are filtering all the values of elements in the map object to get the elements with the value equal to done (see line 14).

On line 15, the length property is used to get the count of the special elements.

What to learn next

Map and set are valuable additions to JavaScript, and they will make your code cleaner and easier to maintain. Now that you have a solid understanding of map and set, you're ready to tackle other features added in ES6 and beyond.

Some concepts to cover next to modernize your JavaScript are:

  • Async and Await (promises)
  • Metaprogramming
  • Object literals
  • Arrow functions
  • and more

To get up to speed on JavaScript's features, check out Educative's course Rediscovering JavaScript: ES6, ES7 & ES8. This course covers the modern JavaScript features to make your code elegant, concise, expressive, and less error prone. You will start by learning the basic features of modern JavaScript, and in the second half, you will dive deep into complex features, like destructuring, literals, inheritance, modules, promises, and metaprogramming.

By the end of this course, you will be able to add new features with minimum effort and write code more efficiently!

Happy learning!

Continue reading about JavaScript

Top comments (1)

Collapse
 
juanitafalbo profile image
JuanitaFalbo

Can you map a set in JavaScript? Kerala Vasiyam Specialist in Salem