left-icon

ECMAScript 6 Succinctly®
by Matthew Duffield

Previous
Chapter

of
A
A
A

CHAPTER 12

Map, Set

Map, Set


Maps and Sets are a welcome to JavaScript. Sets and Maps provide an efficient data structure for common algorithms.

Map

In JavaScript, it has always been a pain mapping objects or primitive values from/to other objects or primitive values. In ES5, the best you could do was map from strings to arbitrary values. The Map data structure in ES6 lets you use arbitrary values as keys.

Let’s look at a quick example of this:

Code Listing: 159

let map = new Map();

   

map.set('foo', 123);

console.log(map.get('foo'));

console.log(map.has('foo'));

console.log(map.delete('foo'));

console.log(map.has('foo'));

Here is the output from the preceding code:

Code Listing: 160

123

true

true

false

Let’s look at another example:

Code Listing: 161

let map = new Map();

map.set('foo', true);

map.set('bar', false);

console.log(map.size);

map.clear();

console.log(map.size);

Here is the output from the preceding code:

Code Listing: 162

2

0

Setting up a map

You can set up a map via an iterable over key-value pair. One way is to use an array as follows:

Code Listing: 163

let map = new Map([

  [1, 'one'],

  [2, 'two'],

  [3, 'three']

]);

Another way is to chain the set method like so:

Code Listing: 164

let map = new Map()

  .set(1, 'one')

  .set(2, 'two')

  .set(3, 'three');

Iterating over a map

We have several options when iterating over a map. First, consider the following map definition:

Code Listing: 165

let map = new Map([

  [1, 'one'],

  [2, 'two'],

  [3, 'three']

]);

Now, let’s take a look at how we can iterate over this map. The first example will iterate over the keys:

Code Listing: 166

for (let key of map.keys()) {

  console.log(key);

}    

We can also iterate over the values:

Code Listing: 167

for (let value of map.values()) {

  console.log(value);

}

We can iterate over the entries:

Code Listing: 168

for (let entry of map.entries()) {

  console.log(entry[0], entry[1]);

}

We can obtain the key/value pair of the entries:

Code Listing: 169

for (let [key, value] of map.entries()) {

  console.log(key, value);

}

Finally, because entries is an iterator itself, we can rewrite the previous in a more terse manner:

Code Listing: 170

for (let [key, value] of map.entries()) {

  console.log(key, value);

}

Spreading a map

The spread operator (…) turns an iterable into the arguments of a function or parameter call. We have several options when iterating over a map. Thus, we can easily spread our map like the following:

Code Listing: 171

let map = new Map([

  [1, 'one'],

  [2, 'two'],

  [3, 'three']

]);

let arr = [...map.keys()];

console.log(arr);

The preceding code generates the following output:

Code Listing: 172

[ 1, 2, 3 ]

WeakMap

A WeakMap is a map that doesn’t prevent its keys from being garbage-collected. That means that you can associate data with objects without worrying about memory leaks.

A WeakMap is a data structure whose keys must be objects and whose values can be arbitrary values. It has the same API as Map, with one significant difference: you can’t iterate over the contents. You can’t iterate over the keys, nor the values, nor the entries. You also can’t clear a WeakMap.

Let’s look at the following example:

Code Listing: 173

let _counter = new WeakMap();

let _action = new WeakMap();

class Countdown {

  constructor(counter, action) {

    _counter.set(this, counter);

    _action.set(this, action);

  }

  dec() {

    let counter = _counter.get(this);

    if (counter < 1) return;

    counter--;

    _counter.set(this, counter);

    if (counter === 0) {

      _action.get(this)();

    }

  }

}

let c = new Countdown(2, () => console.log('DONE'));

c.dec();

c.dec();

The preceding code uses WeakMaps _counter and _action to store private data. This is very elegant in that these WeakMaps are “holding” a reference to the Countdown class, yet they are doing so in a weak manner and, thus, are memory leak safe.

The preceding code generates the following output:

Code Listing: 174

DONE

Set

Set works with arbitrary values, is fast, and even handles NaN correctly.

Let’s take a look at an example:

Code Listing: 175

let set = new Set();

set.add('red');

console.log(set.has('red'));

set.delete('red');

console.log(set.has('red'));

Here is the output from the preceding code:

Code Listing: 176

true

false

Let’s look at another example:

Code Listing: 177

let set = new Set();

set.add('red');

set.add('green');

console.log(set.size);

set.clear();

console.log(set.size);

Here is the output from the preceding code:

Code Listing: 178

2

0

Setting up a set

You can set up a set via an iterable over the elements that make up the set. One way is to use an array as follows:

Code Listing: 179

let set = new Set(['red', 'green', 'blue']);

Another way is to chain the set method like so:

Code Listing: 180

let set = new Set()

     .add('red')

     .add('green')

     .add('blue');

Values

Like maps, elements are compared similarly to ===, with the exception of NaN being like any other value. Consider the following:

Code Listing: 181

let set = new Set([NaN]);

console.log(set.size);

console.log(set.has(NaN));

Executing the preceding code produces the following output:

Code Listing: 182

1

true

Iterating over a set

Sets are iterable, and the for-of loop works exactly as you would expect. Consider the following example:

Code Listing: 183

let set = new Set(['red', 'green', 'blue']);

for (let x of set) {

  console.log(x);

}

Executing the preceding code produces the following output:

Code Listing: 184

red

green

blue

Sets preserve iteration order. That is, elements are always iterated over in the order in which they were inserted.

Now, let’s look at an example using the spread operator (). As you know it works with iterables and thus allows you to convert a set into an array:

Code Listing: 185

let set = new Set(['red', 'green', 'blue']);

let arr = [...set];

console.log(arr);

Executing the preceding code produces the following output:

Code Listing: 186

[ 'red', 'green', 'blue' ]

What about going from array to set? Take a look at the following example:

Code Listing: 187

let arr = [3, 5, 2, 2, 5, 5];

let unique = [...new Set(arr)];

for (let x of unique) {

  console.log(x);

}

The preceding code produces the following output:

Code Listing: 188

3

5

2

As you may have noticed, a set does not have duplicate values in it.

Mapping and filtering a set

Compared to arrays, sets don’t have the methods map() and filter(). However, you can do a simple trick of converting them to arrays and then back again. Consider the following example:

Code Listing: 189

let set = new Set([1, 2, 3]);

set = new Set([...set].map(x => x * 2));

// Resulting set: {2, 4, 6}

Here is an example using filter:

Code Listing: 190

let set = new Set([1, 2, 3, 4, 5]);

set = new Set([...set].filter(x => (x % 2) == 0));

// Resulting set: {2, 4}

WeakSet

A WeakSet is a set that doesn’t prevent its elements from being garbage-collected. A WeakSet doesn’t allow for iteration, looping, or clearing.

Given that you can’t iterate over their elements, there are not very many use cases for WeakSets. They enable you to mark objects and to associate them with Boolean values.

WeakSet API

WeakSets only have three methods, and all of them work the same as the set methods:

  • add(value)
  • has(value)
  • delete(value)
Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.