Skip to content
Theory · Map, Set and JSON · lesson 3 of 9

The Set collection

In the lesson on Map the cloakroom stored "token — coat" pairs. Sometimes you need no pair, and only one thing matters: has this guest come already? Picture a guard at the door with a guest list. They write each name down once, and to the question "Has Anna arrived?" they answer by looking at the list.

In an array you have to keep such a list by hand: check includes before every push. The built-in collection Set makes this check by itself. The expression new Set() creates an empty collection:

JavaScript
const tags = new Set();
tags.add("js");
tags.add("css");
tags.add("js");

console.log(tags.size); // 2
console.log(tags.has("css"), tags.has("html")); // true false

Let's go step by step:

  1. new Set() creates the empty collection tags.
  2. The first two add calls store "js" and "css".
  3. The third add with "js" changes nothing: that value is already there.
  4. That is why size is 2.
  5. has("css") gives true, and has("html") gives false.

What a Set can do

A Set stores separate values, without keys, and holds each value only once. Calling add again with the same value changes nothing.

A Set has:

  • the method has, which tells whether a value is in the collection;
  • the method delete, which removes a value;
  • the property size, which holds the number of values.

Unlike includes on an array, has does not go through the values one by one. So a large collection is checked faster. The guard, too, does not read the whole list from the top but goes straight to the right line.

Such a collection fits when only one thing matters: whether a value has occurred before. Examples are visited pages, issued numbers or the tags of an article.

Which values are equal

A Set compares values the same way as the === operator. The number 3 and the string "3" are different for it, so both stay. There is one difference from ===: NaN === NaN gives false, but a Set counts two NaN values as one value.

Objects are compared by reference, and two objects with the same properties are two different values:

JavaScript
const values = new Set([3, "3", 3, NaN, NaN]);
console.log(values.size); // 3

const points = new Set([{ x: 1 }, { x: 1 }]);
console.log(points.size); // 2

The expression new Set(arr) with an array inside adds its elements one by one, as if add were called for each. That is why repeats from the array do not reach the collection:

  • values keeps 3, "3" and one NaN, three values in all;
  • points holds two different objects, although their properties are the same.

This is where the guest-list analogy stops working. The guard would recognize two guests with one name and write down one. A Set of objects looks not at the properties but at the object itself: two { x: 1 } literals create two different objects.

An array without repeats

A Set remembers the order of adding, and a for…of loop goes over the values in that order. Spread syntax turns a Set back into an array. Together, new Set and spread syntax give an array without repeats. Each value stands where it first occurred, and the repeats disappear:

JavaScript
const nums = [3, 1, 3, 2, 1];
const seen = new Set(nums);

for (const n of seen) {
    console.log(n);
}
// 3
// 1
// 2

console.log([...seen].join(" ")); // 3 1 2

The original array nums does not change: new Set(nums) only reads its elements.

Interviewers like to ask about this trick: "How do you remove repeats from an array in one line?" The answer is [...new Set(nums)].

Common mistake

A value of a Set was read by index, like an array element:

JavaScript
const seen = new Set(["a", "b"]);

console.log(seen[0]); // ✗

The program prints undefined. A Set has an order but no indexes, and seen.length gives undefined too. A Set exists to check quickly whether a value is in it, not to take values by position. To take a value by its position, turn the Set into an array first: [...seen][0] gives "a".

Key points

  • A Set holds each value once: a repeated add changes nothing.
  • has checks whether a value is there, and size holds the number of values.
  • Values are compared as with ===, except that two NaN values count as one, and objects are compared by reference.
  • [...new Set(nums)] gives an array without repeats, in the order of first appearance.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way