Set
===

Объект **`Set`** позволяет хранить уникальные значения любого типа, будь то примитивы или ссылки на объекты.

Описание
--------

Объекты "Set" - это коллекция значений. Значение в `Set` **может встречаться только один раз**; оно уникально в коллекции. Вы можете перебирать элементы набора в порядке вставки. Порядок *вставки* соответствует порядку, в котором каждый элемент был успешно вставлен в коллекцию методом `add()` (то есть, когда был вызван `add()`, в наборе ещё не было такого элемента).

Спецификация требует реализации наборов, "которые в среднем обеспечивают время доступа, сублинейное количеству элементов в коллекции". Следовательно, он может быть представлен внутренне в виде хэш-таблицы (с поиском O(1)), дерева поиска (с поиском O(log(N))) или любой другой структуры данных, при условии, что сложность выше, чем O(N).

### Сравнение значений

При добавлении нового элемента в Set происходит проверка, добавлялся ли такой элемент ранее. Эта проверка использует специальный алгоритм сравнения значений SameValueZero. (Раньше использовался алгоритм SameValue, в котором значения `0` и `-0` считаются разными. Смотрите браузерную поддержку ниже). Это означает, что `NaN` считается равным `NaN` (не смотря на то что `NaN !== NaN`), а все другие значения считаются равными в соответствии с семантикой оператора `===`.

### Производительность

Метод `has` проверяет наличие значения в `Set` используя алгоритм, который в среднем работает быстрее поэлементного перебора добавленных ранее элементов. В частности этот алгоритм работает быстрее чем метод [`Array.prototype.includes`](/ru/docs/Web/JavaScript/Reference/Global\_Objects/Array/includes на массиве, который хранит столько же элементов сколько сравниваемый объект `Set`.

Конструктор
-----------

`Set()`

Создаёт новый объект `Set`.

Статические свойства
--------------------

`get Set[@@species]` (en-US)

Функция-конструктор, которая используется для создания производных объектов.

Свойства экземпляра
-------------------

`Set.prototype[@@toStringTag]`

Начальное значение `@@toStringTag` свойства - строка `"Set"`. Это значение используется в `Object.prototype.toString()`.

`Set.prototype.size`

Возвращает количество значений в объекте `Set`.

Методы экземпляра
-----------------

* `Set.prototype.add()`
    + : Вставляет новый элемент с указанным значением в объект `Set`, если в `Set` ещё нет элемента с таким же значением.
* `Set.prototype.clear()`
    + : Удаляет все значения из объекта `Set`.
* `Set.prototype.delete()`
    + : Удаляет элемент, связанный с переданным значением, и возвращает логическое значение, подтверждающее, был ли элемент успешно удалён или нет. `Set.prototype.has(value)` впоследствии вернет `false`.
* `Set.prototype.has()`
    + : Возвращает логическое значение, утверждающее, присутствует ли элемент с заданным значением в объекте `Set` или нет.
* `Set.prototype[@@iterator]()` (en-US)
    + : Возвращает новый объект итератора, который выдает **значения** для каждого элемента в объекте `Set` в порядке вставки.
* `Set.prototype.values()`
    + : Возвращает новый объект итератора, который выдает **значения** для каждого элемента в объекте `Set` в порядке вставки.
* `Set.prototype.keys()` (en-US)
    + : Аналог `Set.prototype.values()`.
* `Set.prototype.entries()`
    + : Возвращает новый объект итератора, который содержит **массив `[value, value]`** для каждого элемента в объекте `Set` в порядке вставки.Этот метод похож на соответствующий метод объекта `Map` (en-US), но в случае `Set` ключ (*key*) каждой записи совпадает с её значением (*value*).
* `Set.prototype.forEach()`
    + : Вызывает `callbackFn` один раз для каждого значения, присутствующего в объекте `Set` в порядке вставки. Если указан параметр `thisArg`, он будет использоваться в качестве значения `this` для каждого вызова `callbackFn`.
Примеры
-------

### Использование объекта Set


```js
const mySet1 = new Set();

mySet1.add(1); // Set(1) { 1 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add(5); // Set(2) { 1, 5 }
mySet1.add("текст"); // Set(3) { 1, 5, 'текст' }
const o = { a: 1, b: 2 };
mySet1.add(o);

mySet1.add({ a: 1, b: 2 }); // o ссылается на другой объект, так что это нормально

mySet1.has(1); // true
mySet1.has(3); // false, поскольку 3 не был добавлен в набор
mySet1.has(5); // true
mySet1.has(Math.sqrt(25)); // true
mySet1.has("Текст".toLowerCase()); // true
mySet1.has(o); // true

mySet1.size; // 5

mySet1.delete(5); // удаляет элемент 5 из set
mySet1.has(5); // false, 5 был удалён

mySet1.size; // 4, поскольку мы удалили одно значение

mySet1.add(5); // Set(5) { 1, "текст", {...}, {...}, 5 } - ранее удаленный элемент будет добавлен как новый элемент, он не сохранит свое первоначальное положение до удаления

console.log(mySet1); // Set(5) { 1, "текст", {…}, {…}, 5 }
```

### Итерация по объекту Set

Итерация наборов проходит по элементам в порядке вставки.


```js
for (const item of mySet1) {
  console.log(item);
}
// 1, "текст", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.keys()) {
  console.log(item);
}
// 1, "текст", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

for (const item of mySet1.values()) {
  console.log(item);
}
// 1, "текст", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// ключ и значение здесь одинаковы
for (const [key, value] of mySet1.entries()) {
  console.log(key);
}
// 1, "текст", { "a": 1, "b": 2 }, { "a": 1, "b": 2 }, 5

// Преобразуем объект Set в объект Array с помощью Array.from
const myArr = Array.from(mySet1); // [1, "текст", {"a": 1, "b": 2}, {"a": 1, "b": 2}, 5]

// следующее также будет работать, если запустить его в HTML-документе
mySet1.add(document.body);
mySet1.has(document.querySelector("body")); // true

// преобразование между Set и Array
const mySet2 = new Set([1, 2, 3, 4]);
console.log(mySet2.size); // 4
console.log([...mySet2]); // [1, 2, 3, 4]

// пересечение можно представить следующим образом
const intersection = new Set([...mySet1].filter((x) => mySet2.has(x)));

// разность можно представить следующим образом
const difference = new Set([...mySet1].filter((x) => !mySet2.has(x)));

// итерироваться можно с помощью forEach()
mySet2.forEach((value) => {
  console.log(value);
});
// 1
// 2
// 3
// 4
```

### Реализация базовых операций Set


```js
function isSuperset(set, subset) {
  for (const elem of subset) {
    if (!set.has(elem)) {
      return false;
    }
  }
  return true;
}

function union(setA, setB) {
  const _union = new Set(setA);
  for (const elem of setB) {
    _union.add(elem);
  }
  return _union;
}

function intersection(setA, setB) {
  const _intersection = new Set();
  for (const elem of setB) {
    if (setA.has(elem)) {
      _intersection.add(elem);
    }
  }
  return _intersection;
}

function symmetricDifference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    if (_difference.has(elem)) {
      _difference.delete(elem);
    } else {
      _difference.add(elem);
    }
  }
  return _difference;
}

function difference(setA, setB) {
  const _difference = new Set(setA);
  for (const elem of setB) {
    _difference.delete(elem);
  }
  return _difference;
}

// Примеры
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 3]);
const setC = new Set([3, 4, 5, 6]);

isSuperset(setA, setB); // true
union(setA, setC); // Set {1, 2, 3, 4, 5, 6}
intersection(setA, setC); // Set {3, 4}
symmetricDifference(setA, setC); // Set {1, 2, 5, 6}
difference(setA, setC); // Set {1, 2}
```

### Связь с объектами массива


```js
const myArray = ["value1", "value2", "value3"];

// Используйте обычный конструктор Set для преобразования массива в набор
const mySet = new Set(myArray);

mySet.has("value1"); // true

// Используйте синтаксис spread для преобразования набора в массив.
console.log([...mySet]); // Покажет вам точно такой же массив, как и myArray
```

### Удаление повторяющиеся элементов из массива


```js
const numbers = [2, 3, 4, 4, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 5, 32, 3, 4, 5];

console.log([...new Set(numbers)]); // [2, 3, 4, 5, 6, 7, 32]
```

### Связь со строками


```js
// Чувствительность к регистру (коллекция будет содержать "F" и "f")
new Set("Firefox"); // Set(7) [ "F", "i", "r", "e", "f", "o", "x" ]

// Пропуск дубликатов ("f" встречается два раза в строке, но в коллекции будет только один раз)
new Set("firefox"); // Set(6) [ "f", "i", "r", "e", "o", "x" ]
```

### Используйте Set для обеспечения уникальности списка значений


```js
const array = Array.from(document.querySelectorAll("[id]")).map((e) => e.id);

const set = new Set(array);
console.assert(set.size === array.length);
```

Спецификации
------------

| Specification |
| --- |
| ECMAScript Language Specification # sec-set-objects |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* Полифил `Set` в `core-js`
* map (en-US)
* `WeakMap`
* `WeakSet`