Set

Об'єкт Set (множина) дає змогу зберігати унікальні значення будь-якого типу, як примітивні, так і посилання на об'єкти.

Опис

Об'єкти Set є колекціями значень. Конкретне значення в множині може зустрітись лише раз, воно є неповторним у межах колекції цієї множини. Ітерування елементів Set відбувається в порядку додання. Порядок додання відповідає порядкові, в якому кожен елемент був успішно вставлений у множину методом add() (тобто коли до виклику add() у множині не було ідентичного елемента).

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

Рівність значень

Рівність значень заснована на алгоритмі SameValueZero. (Раніше використовувався алгоритм SameValue, котрий розглядав 0 і -0 як різні значення. Перевірте сумісність із браузерами.) Це означає, що значення NaN вважається рівним іншому значенню NaN (попри те, що NaN !== NaN), а всі решта значень перевіряються на рівність згідно з семантикою оператора ===.

Швидкодія

Метод has перевіряє, чи присутнє значення в множині, використовуючи підхід, що в середньому є швидшим за перевірку більшості елементів, що були до того додані до множини. Для прикладу, це в середньому швидше, ніж метод Array.prototype.includes, коли масив має length, що дорівнює значенню size множини.

Set-подібні API браузера

Set-подібні об'єкти браузера (або "множиноподібні об'єкти") - це інтерфейси API Вебу, що з багатьох боків поводяться подібно до Set.

Як і в Set, елементи можуть бути ітеровані в тому ж порядку, в якому вони додані до такого об'єкта. Також Set-подібні об'єкти та Set мають властивості та методи, що поділяють однакові назви та поведінку. Проте, на відміну від Set, вони дозволяють лише певний наперед визначений тип для кожного зі своїх елементів.

Дозволені типи задані у визначенні специфікації IDL. Наприклад, GPUSupportedFeatures – це Set-подібний об'єкт, що повинен використовувати рядки як ключі та значення. Це визначено в специфікації IDL нижче:

interface GPUSupportedFeatures {
  readonly setlike<DOMString>;
};

Set-подібні об'єкти – або доступні лише для зчитування, або також і для запису (дивіться ключове слово readonly в IDL вище).

Ці методи та властивості мають таку ж логіку, як рівносильні аналоги в Set, окрім обмеження щодо типів елементів.

Нижче – приклади Set-подібних браузерних об'єктів лише для зчитування:

А це – приклади записних Set-подібних браузерних об'єктів:

Конструктор

Set()

Створює новий об'єкт Set.

Статичні властивості

Set[@@species]

Функція-конструктор, що використовується для створення похідних об'єктів.

Властивості примірника

Ці властивості означені на Set.prototype і є спільними для всіх примірників Set.

Set.prototype.constructor

Функція-конструктор, що створила об'єкт-примірник. Для примірників Set початковим значенням є конструктор Set.

Set.prototype.size (розмір)

Повертає кількість значень, присутніх в об'єкті Set.

Set.prototype[@@toStringTag]

Початкове значення властивості @@toStringTag – рядок "Set". Ця властивість використовується в Object.prototype.toString().

Методи примірника

Set.prototype.add() (додати)

Додає новий елемент зі вказаним значенням до об'єкта Set, якщо елемента з таким само значенням іще в Set немає.

Set.prototype.clear() (очистити)

Усуває з об'єкта Set всі значення.

Set.prototype.delete() (видалити)

Усуває елемент, пов'язаний із value, та повертає булеве значення, що вказує, чи був елемент успішно усунутий. Після цього Set.prototype.has(value) поверне false.

Set.prototype.entries() (записи)

Повертає новий об'єкт-ітератор, що містить масив із [value, value] для кожного елемента в об'єкті Set, у порядку їх додання. Це подібно до об'єкта Map, якби ключ кожного запису був би водночас власним значенням.

Set.prototype.forEach() (для кожного)

Один раз викликає callbackFn для кожного значення, присутнього в об'єкті Set, у порядку їх додання. Якщо наданий параметр thisArg, то він використовуватиметься при кожному виклику callbackFn як значення this.

Set.prototype.has() (має)

Повертає булеве значення, що вказує, чи є елемент із даним значенням в об'єкті Set.

Set.prototype.keys() (ключі)

Псевдонім для Set.prototype.values().

Set.prototype.values() (значення)

Повертає новий об'єкт-ітератор, що видає значення для кожного елемента в об'єкті Set, у порядку їх додання.

Set.prototype@@iterator

Повертає новий об'єкт-ітератор, що видає значення для кожного елемента в об'єкті Set, у порядку їх додання.

Приклади

Використання об'єкта Set

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 з множини
mySet1.has(5); // false, 5 була усунута

mySet1.size; // 4, оскільки одне значення щойно було усунуто

mySet1.add(5); // Set(5) { 1, 'якийсь текст', {...}, {...}, 5 } - видалений раніше елемент буде доданий заново; він не збереже позиції, що мав до видалення

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

Ітерування множин

Ітерування множин обходить елементи в порядку їх додавання.

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

// За допомогою Array.from перетворює об'єкт Set на об'єкт Array
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

Реалізація базових дій з множинами

// Чи є надмножиною
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}

Зв'язок з масивами

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

// Використати звичайний конструктор Set, щоб перетворити масив на множину
const mySet = new Set(myArray);

mySet.has("значення1"); // повертає true

// Використати синтаксис розгортання, щоб перетворити множину на масив
console.log([...mySet]); // Покаже точно такий самий масив, як myArray

Усунення дублікатів з масиву

// Використання для усунення дублікатів з масиву

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]

Зв'язок із рядками

const text = "Індія";

const mySet = new Set(text); // Set(5) {'І', 'н', 'д', 'і', 'я'}
mySet.size; // 5

// чутливість до регістру та усунення дублікатів
new Set("Firefox"); // Set(7) { "F", "i", "r", "e", "f", "o", "x" }
new Set("firefox"); // Set(6) { "f", "i", "r", "e", "o", "x" }

Використання множини для пересвідчення щодо унікальності всіх значень у списку

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

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

Специфікації

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
Set
Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
@@iterator Chrome Full support 43
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 30
Safari Full support 9
WebView Android Full support 43
Chrome Android Full support 43
Firefox for Android Full support 36
Opera Android Full support 30
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 0.12.0
@@species Chrome Full support 51
Edge Full support 13
Firefox Full support 41
Internet Explorer No support No
Opera Full support 38
Safari Full support 10
WebView Android Full support 51
Chrome Android Full support 51
Firefox for Android Full support 41
Opera Android Full support 41
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.5.0
Set() constructor Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
new Set(iterable)
Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
Set() without new throws
Chrome Full support 38
Edge Full support 12
Firefox Full support 42
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 42
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
new Set(null)
Chrome Full support 38
Edge Full support 12
Firefox Full support 37
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 37
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
add Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer Partial support 11
footnote
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
clear Chrome Full support 38
Edge Full support 12
Firefox Full support 19
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 19
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
delete Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
entries Chrome Full support 38
Edge Full support 12
Firefox Full support 24
Internet Explorer No support No
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 24
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
forEach Chrome Full support 38
Edge Full support 12
Firefox Full support 25
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 25
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
has Chrome Full support 38
Edge Full support 12
Firefox Full support 13
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
Key equality for -0 and 0
Chrome Full support 38
Edge Full support 12
Firefox Full support 29
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 29
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 4.0.0
size Chrome Full support 38
Edge Full support 12
Firefox Full support 19
footnote
Internet Explorer Full support 11
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 19
footnote
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
values Chrome Full support 38
Edge Full support 12
Firefox Full support 24
Internet Explorer No support No
Opera Full support 25
Safari Full support 8
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 24
Opera Android Full support 25
Safari on iOS Full support 8
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0

Дивіться також