DEV Community

Zsolt Gomori
Zsolt Gomori

Posted on

An Overview of Map and Set in JavaScript

Looking for comparisons? Refer to the articles below:

Map

Creating an instance

1) Using the constructor:

const colors = new Map();
Enter fullscreen mode Exit fullscreen mode

2) Passing an iterable:

const colors = new Map([
  ["#ff0000", "red"],
  ["#0000ff", "blue"]
]);

// => colors: {"#ff0000" => "red", "#0000ff" => "blue"}
Enter fullscreen mode Exit fullscreen mode

3) Using the set() method which is chainable:

const colors = new Map()
  .set("#ff0000", "red")
  .set("#0000ff", "blue");

// => colors: {"#ff0000" => "red", "#0000ff" => "blue"}
Enter fullscreen mode Exit fullscreen mode

Methods

1) Reading and writing:

const colors = new Map([
  ["#ff0000", "red"]
]);

colors.get("#ff0000");
// => "red"

colors.get("#0000ff");
// => undefined
Enter fullscreen mode Exit fullscreen mode

Writing has already been covered, though, but for the sake of completeness:

const colors = new Map();

colors.set("#0000ff", "blue");
// colors: {"#0000ff" => "blue"}
Enter fullscreen mode Exit fullscreen mode

2) Checking if the entry with the given key exists:

const colors = new Map().set("#ff0000", "red");

colors.has("#ff0000");
// => true
Enter fullscreen mode Exit fullscreen mode

3) Getting the keys and values:

const colors = new Map().set("#ff0000", "red");

for (const hex of colors.keys()) {
  console.log(hex);
}

// => "#ff0000"

for (const color of colors.values()) {
  console.log(color);
}

// => "red"

for (const [hex, color] of colors.entries()) {
  console.log(hex, "=>", color);
}

// => "#ff0000 => red"
Enter fullscreen mode Exit fullscreen mode

4) Removing a single entry:

The delete() method returns a boolean according to the operation's result.

const colors = new Map().set("#ff0000", "red");

colors.delete("#ff0000");
// => true

colors.delete("foo");
// => false
Enter fullscreen mode Exit fullscreen mode

5) Removing all entries:

const colors = new Map().set("#ff0000", "red");

colors.clear();
// => colors: {}
Enter fullscreen mode Exit fullscreen mode

Properties

When it comes to retrieving the size of a map (that is, the number of entries it has), it's more convenient than that of an object:

const colors = new Map([
  ["#ff0000", "red"]
]);

console.log(colors.size);
// => 1

colors.set("#0000ff", "blue");

console.log(colors.size);
// => 2
Enter fullscreen mode Exit fullscreen mode

Tips & Tricks

1) Converting between Maps and Objects

The conversion is possible so long as the map consists of keys of types of strings and symbols.

const colors = new Map([
  ["#ff0000", "red"]
]);

const obj = Object.fromEntries(colors);
// => obj: {"#ff0000": "red"}
Enter fullscreen mode Exit fullscreen mode

2) Mapping and filtering

Steps:

  1. Convert the Map to an Array.
  2. Filter or map the array.
  3. Convert the result back to a Map.
const numbers = new Map([
  [1, "a"],
  [2, "b"],
  [3, "c"]
]);

const mappedNumbers = new Map( // 3
  [...numbers] // 1
    .map( // 2
      ([number, char]) => [number * 10, char]
    )
);
// => mappedNumbers: {10 => "a", 20 => "b", 30 => "c"}
Enter fullscreen mode Exit fullscreen mode
const numbers = new Map([
  [1, "a"],
  [2, "b"],
  [3, "c"]
]);

const filteredNumbers = new Map( // 3
  [...numbers] // 1
    .filter( // 2
      ([number]) => number > 2
    )
);
// => filteredNumbers: {3 => "c"}
Enter fullscreen mode Exit fullscreen mode

3) Key equality is based on sameValueZero

NaN keys are considered to be equal:

const map = new Map();

map.set(NaN, "foo");
map.get(NaN);
// => "foo"

map.set(NaN, "bar");
map.get(NaN);
// => "bar"
Enter fullscreen mode Exit fullscreen mode

But object keys are always different:

const map = new Map().set({}, "foo").set({}, "bar");

console.log(map.size);
// => 2
Enter fullscreen mode Exit fullscreen mode

Set

Creating an instance

1) Using the constructor:

const set = new Set();
Enter fullscreen mode Exit fullscreen mode

2) Passing an iterable:

const numbers = new Set([1, 2, 3]);
// => numbers: {1, 2, 3}

const chars = new Set("123");
// => chars: {"1", "2", "3"}
Enter fullscreen mode Exit fullscreen mode

3) Using the add() method which, similarly to set(), is chainable:

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);
// => numbers: {1, 2, 3}
Enter fullscreen mode Exit fullscreen mode

Methods

1) Reading and writing values:

Keep in mind the caveat that there's no random access to an element in a Set (citing from ES6 — Set vs Array — What and when?):

More important, because the Array data is stored in consecutive memory, the CPU will be able to access the data much faster due to pre-fetching. Hence in general accessing an elements in Array (one after the other such as in a for loop) is quicker and more efficient if you compared to other type of abstract data types.

There are workarounds to locate specific elements though. For instance, to get the first element of a set you can grab an iterator from values() and then call the next() method (original solution from StackOverflow):

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

const firstElement = numbers.values().next().value;
// => firstElement: 1
Enter fullscreen mode Exit fullscreen mode

2) Checking if an element is a member of the collection:

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

numbers.has(1);
// => true

numbers.has(4);
// => false
Enter fullscreen mode Exit fullscreen mode

3) Removing a single element:

Just like the delete() method of Map, the returned boolean value indicates if the operation was successful:

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

numbers.delete(1);
// => true

numbers.delete(4);
// => false
Enter fullscreen mode Exit fullscreen mode

4) Removing all elements:

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

numbers.clear();
// => numbers: {}
Enter fullscreen mode Exit fullscreen mode

Properties

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

console.log(numbers.size);
// => 3
Enter fullscreen mode Exit fullscreen mode

Tips & Tricks

1) Removing duplicated elements from an Array:

const arr = [1, 1, 2, 3, 4, 4, 5];

const filteredArr = [...new Set(arr)];
// => filteredArr: [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

2) Iteration:

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

for (const number of numbers) {
  console.log(number);
}
// => 1
// => 2
// => 3
Enter fullscreen mode Exit fullscreen mode

3) As with Map keys, NaN elements are considered to be equal:

const nanSet = new Set([NaN, NaN, NaN, NaN]);

console.log(nanSet.size);
// => 1
Enter fullscreen mode Exit fullscreen mode

But objects are always different:

const objSet = new Set([{}, {}, {}]);

console.log(objSet.size);
// => 3
Enter fullscreen mode Exit fullscreen mode

4) Mapping and filtering

const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

const mappedNumbers = new Set([...numbers].map(number => number * 2));
// => mappedNumbers: {2, 4, 6}
Enter fullscreen mode Exit fullscreen mode
const numbers = new Set()
  .add(1)
  .add(2)
  .add(3);

const filteredNumbers = new Set([...numbers].filter(number => number > 2));
// => filteredNumbers: {3}
Enter fullscreen mode Exit fullscreen mode

5) Union

const a = new Set(["foo", "bar"]);
const b = new Set(["foo", "buzz"]);

const union = new Set([...a, ...b]);
// => union: {"foo", "bar", "buzz"}
Enter fullscreen mode Exit fullscreen mode

6) Intersection

const a = new Set(["foo", "bar"]);
const b = new Set(["foo", "buzz"]);

const intersection = new Set([...a].filter(val => b.has(val)));
// => intersection: {"foo"}
Enter fullscreen mode Exit fullscreen mode

7) Difference

const a = new Set(["foo", "bar"]);
const b = new Set(["foo", "buzz"]);

const difference = new Set([...a].filter(val => !b.has(val)));
// => difference: {"bar"}
Enter fullscreen mode Exit fullscreen mode

I hope you found the article helpful! Until next time!

Psst. Would you like to add something? I'd like to hear about it. Just leave a comment or drop me a message on Twitter.

Top comments (0)