Клавиша / esc

Set

Коллекция для хранения уникальных значений.

Время чтения: 5 мин

Кратко

Скопировано

Set (по-русски говорят множество) — коллекция для хранения уникальных значений любого типа. Одно и то же значение нельзя добавить в Set больше одного раза.

Set — это неиндексированная коллекция, положить элемент в коллекцию можно, но достать нельзя. По элементам коллекции можно итерироваться.

Основные методы для работы с коллекцией:

Содержит свойство size для получения количества элементов в коллекции.

Пример

Скопировано
        
          
          const uniqueIds = new Set()uniqueIds.add(123)uniqueIds.add(456)uniqueIds.add(111)uniqueIds.add(123)console.log(uniqueIds.size)// 3console.log(uniqueIds.has(111))// trueuniqueIds.delete(111)console.log(uniqueIds.size)// 2uniqueIds.clear()console.log(uniqueIds.size)// 0
          const uniqueIds = new Set()

uniqueIds.add(123)
uniqueIds.add(456)
uniqueIds.add(111)
uniqueIds.add(123)

console.log(uniqueIds.size)
// 3

console.log(uniqueIds.has(111))
// true

uniqueIds.delete(111)
console.log(uniqueIds.size)
// 2

uniqueIds.clear()
console.log(uniqueIds.size)
// 0

        
        
          
        
      

Как понять

Скопировано

Коллекция Set может хранить произвольный набор значений — нет разницы, хранить в коллекции примитивные типы, объекты или массивы. Set гарантирует, что одно и то же значение не может попасть в коллекцию больше одного раза. Если нам нужно получить коллекцию уникальных значений из массива неуникальных — Set один из способов этого достичь.

Добавляйте элемент в массив и множество, чтобы понять разницу:

Открыть демо в новой вкладке

Создание коллекции

Скопировано

Коллекция создаётся при помощи конструктора.

Можно создать пустой Set:

        
          
          const set = new Set()console.log(set.size)// 0
          const set = new Set()

console.log(set.size)
// 0

        
        
          
        
      

Или сразу добавить в него элементы. Для этого нужно передать в конструктор итерируемый список значений. Обычно это массив:

        
          
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])console.log(filled.size)// 4
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])

console.log(filled.size)
// 4

        
        
          
        
      

Работа с коллекцией

Скопировано

Set предоставляет небольшой набор методов, но их достаточно в большинстве случаев.

Добавляют элемент в Set с помощью метода add(), а удаляют с помощью delete(). В оба метода передаётся элемент, который нужно добавить или удалить:

        
          
          const filled = new Set([1, 2, 3, '3', 3, 'hello'])filled.add(100)filled.delete(1)
          const filled = new Set([1, 2, 3, '3', 3, 'hello'])

filled.add(100)
filled.delete(1)

        
        
          
        
      

Количество элементов в множестве хранится в свойстве size. Проверить количество элементов в множестве filled:

        
          
          console.log(filled.size)// 5
          console.log(filled.size)
// 5

        
        
          
        
      

Set позволяет проверить, был ли элемент уже добавлен. За это отвечает метод has():

        
          
          const filled = new Set([1, 2, 3, '3', 3, 'hello'])console.log(filled.has(3))// trueconsole.log(filled.has('3'))// trueconsole.log(filled.has('My name'))// false
          const filled = new Set([1, 2, 3, '3', 3, 'hello'])

console.log(filled.has(3))
// true
console.log(filled.has('3'))
// true

console.log(filled.has('My name'))
// false

        
        
          
        
      

Полностью очистить Set можно методом clear(). Технически это то же самое, что и создать новый Set:

        
          
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])filled.clear()console.log(filled.size)// 0
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])
filled.clear()

console.log(filled.size)
// 0

        
        
          
        
      

Обход

Скопировано

Set — это неиндексированная коллекция. В этой структуре данных нет понятия индекса элемента, поэтому нельзя получить произвольный элемент коллекции. В коллекцию можно только положить значение, а получить отдельное значение нельзя.

Основной инструмент работы с Set — обход коллекции. При обходе коллекции нам гарантируется, что мы будем получать элементы в порядке их добавления в Set, то есть первыми обойдём элементы добавленные раньше всего.

Обход можно организовать двумя способами:

1️⃣ Использовать метод forEach(), который работает аналогично одноимённому методу массива:

        
          
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])filled.forEach(function(value) {  console.log(value)})// 1// 2// 3// 'hello'
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])

filled.forEach(function(value) {
  console.log(value)
})

// 1
// 2
// 3
// 'hello'

        
        
          
        
      

2️⃣ Воспользоваться for...of:

        
          
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])for (let n of filled) {  console.log(n)}// 1// 2// 3// 'hello'
          const filled = new Set([1, 2, 3, 3, 3, 'hello'])

for (let n of filled) {
  console.log(n)
}

// 1
// 2
// 3
// 'hello'

        
        
          
        
      

Особенности работы с непримитивными типами

Скопировано

Set использует строгое сравнение для проверки, есть ли элемент в коллекции или нет. Добавление примитивных значений разных типов будет работать как ожидается, приведения типов нет. При добавлении числа и строки с этим числом оба добавятся в коллекцию:

        
          
          const set = new Set()set.add(1)set.add('1')console.log(set.size)// 2
          const set = new Set()

set.add(1)
set.add('1')

console.log(set.size)
// 2

        
        
          
        
      

Непримитивные типы хранятся по ссылке, поэтому Set будет проверять что мы действительно пытаемся добавить тот же самый объект в коллекцию или нет. Это может казаться нелогичным, потому что объекты могут выглядеть одинаково, но не быть одним и тем же объектом (то есть у них разные адреса в памяти):

Создадим два различных объекта с одинаковым набором свойств. Сравним их друг с другом и с собой:

        
          
          const cheapShirt = { size: 'L', color: 'white' }const fancyShirt = { size: 'L', color: 'white' }console.log(cheapShirt === fancyShirt)// falseconsole.log(cheapShirt === cheapShirt)// trueconsole.log(fancyShirt === fancyShirt)// true
          const cheapShirt = { size: 'L', color: 'white' }
const fancyShirt = { size: 'L', color: 'white' }

console.log(cheapShirt === fancyShirt)
// false
console.log(cheapShirt === cheapShirt)
// true
console.log(fancyShirt === fancyShirt)
// true

        
        
          
        
      

Мы создали два разных объекта (фигурные скобки создают новый объект), которые выглядят одинаково, но по факту это разные объекты. Они не равны друг другу — если в один добавить новое свойство, то второй не изменится.

Попробуем добавить эти объекты в Set:

        
          
          const closet = new Set()closet.add(cheapShirt)closet.add(fancyShirt)console.log(closet.size)// 2
          const closet = new Set()
closet.add(cheapShirt)
closet.add(fancyShirt)

console.log(closet.size)
// 2

        
        
          
        
      

Так как это разные объекты, то оба добавились в коллекцию. Если же попробовать добавить их второй раз, то эта операция будет проигнорирована:

        
          
          const closet = new Set()closet.add(cheapShirt)closet.add(fancyShirt)console.log(closet.size)// 2closet.add(cheapShirt)closet.add(cheapShirt)closet.add(fancyShirt)console.log(closet.size)// 2
          const closet = new Set()
closet.add(cheapShirt)
closet.add(fancyShirt)

console.log(closet.size)
// 2

closet.add(cheapShirt)
closet.add(cheapShirt)
closet.add(fancyShirt)

console.log(closet.size)
// 2

        
        
          
        
      

На практике

Скопировано

Николай Лопин советует

Скопировано

🛠 С помощью Set можно легко получить массив уникальных элементов из массива неуникальных с помощью конструктора и спред-синтаксиса:

        
          
          const nonUnique = [1, 2, 3, 4, 5, 4, 5, 1, 1]const uniqueValuesArr = [...new Set(nonUnique)]console.log(uniqueValuesArr)// [1, 2, 3, 4, 5]
          const nonUnique = [1, 2, 3, 4, 5, 4, 5, 1, 1]
const uniqueValuesArr = [...new Set(nonUnique)]

console.log(uniqueValuesArr)
// [1, 2, 3, 4, 5]

        
        
          
        
      

Это не самый оптимальный способ преобразования с точки зрения скорости работы, но самый удобный с точки зрения количества кода.