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
? - How to use
map
- What is
set
? - How to use
set
- Hands-on exercise with
map
- What to learn next
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 aWeakMap
are objects.
To create a new Map
, we use the following syntax:
let map = new Map([iterable]);
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
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}`);
}
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}`));
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));
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']
]);
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
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
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
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);
This piece of code will convert the values of elements to an array:
var roles = [...userRoles.values()];
console.log(roles);
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. ThethisArg
parameter is optional and sets thethis
value for each callback.
-
-
has(key)
: returnstrue
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
Output: 4
We can add elements to an existing set, like below:
names.add('Matt');
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');
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 thedelete()
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
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
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
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"}
And to delete all elements of a set, use the clear()
method:
chars.clear();
console.log(chars); // Set{}
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()));
Other important Set methods
-
new Set(iterable)
: creates a set. -
set.add(value)
: adds a given value and returns the set -
set.has(value)
: returnstrue
if a value exists in the set, otherwise, returnsfalse
. -
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
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!
Top comments (2)
Can you map a set in JavaScript? Kerala Vasiyam Specialist in Salem
You cannot map the set in JS, you have to convert it to an array first.