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 пропонує методи, що дають змогу компонувати множини, як це робиться у математиці. Серед цих методів:
| Метод | Тип поверненого значення | Математичний еквівалент | Діаграма Венна |
|---|---|---|---|
A.difference(B) |
Set |
||
A.intersection(B) |
Set |
||
A.symmetricDifference(B) |
Set |
||
A.union(B) |
Set |
||
A.isDisjointFrom(B) |
Boolean |
||
A.isSubsetOf(B) |
Boolean |
||
A.isSupersetOf(B) |
Boolean |
Щоб бути більш узагальненими, ці методи приймають не лише об'єкти Set, але й будь-що, що є множиноподібним.
Множиноподібні об'єкти
Усі методи композиції множин вимагають того, щоб значення this було справжнім примірником Set, але їх аргументи повинні бути хоча б множиноподібними. Множиноподібний об'єкт – це об'єкт, що має наступне:
- Властивість
size, що містить число. - Метод
has(), який приймає елемент і повертає булеве значення. - Метод
keys(), який повертає ітератор елементів множини.
Наприклад, об'єкти Map є множиноподібними, оскільки вони також мають size, has() і keys(), тому вони поводяться неначе множини ключів, коли використовуються у методах множин:
const a = new Set([1, 2, 3]);
const b = new Map([
[1, "один"],
[2, "два"],
[4, "чотири"],
]);
console.log(a.union(b)); // Set(4) {1, 2, 3, 4}
[!NOTE] Протокол множиноподібності закликає для видачі елементів метод
keys(), а не[Symbol.iterator](). Це зроблено для того, щоб відображення були дієвими множиноподібними об'єктами, адже в випадку відображень ітератор видає записи, проте методhas()приймає ключі. Масиви не є множиноподібними, тому що не мають методаhas()і властивостіsize, а їх методkeys()видає індекси, а не елементи. Об'єктиWeakSetтакож не є множиноподібними, оскільки не мають методаkeys().
Set-подібні API браузера
Set-подібні об'єкти браузера (або "множиноподібні об'єкти") – це інтерфейси API Вебу, що з багатьох боків поводяться подібно до Set.
Як і в Set, елементи можуть бути ітеровані в тому ж порядку, в якому вони додані до такого об'єкта.
Також Set-подібні об'єкти та Set мають властивості та методи, що поділяють однакові назви та поведінку.
Проте, на відміну від Set, вони дозволяють лише певний наперед визначений тип для кожного зі своїх елементів.
Дозволені типи задані у визначенні специфікації IDL.
Наприклад, GPUSupportedFeatures – це Set-подібний об'єкт, що повинен використовувати рядки як ключі та значення.
Це визначено в специфікації IDL нижче:
interface GPUSupportedFeatures {
readonly setlike<DOMString>;
};
Set-подібні об'єкти – або доступні лише для зчитування, або також і для запису (дивіться ключове слово readonly в IDL вище).
Set-подібні об'єкти лише для зчитування мають властивістьsizeі методи:entries(),forEach(),has(),keys(),values()таSymbol.iterator.- Записні
Set-подібні об'єкти на додачу мають такі методи:clear(),delete()таadd().
Ці методи та властивості мають таку ж логіку, як рівносильні аналоги в Set, окрім обмеження щодо типів елементів.
Нижче – приклади Set-подібних браузерних об'єктів лише для зчитування:
А це – приклади записних Set-подібних браузерних об'єктів:
Конструктор
Set()Створює новий об'єкт
Set.
Статичні властивості
Set[Symbol.species]Функція-конструктор, що використовується для створення похідних об'єктів.
Властивості примірника
Ці властивості означені на Set.prototype і є спільними для всіх примірників Set.
Set.prototype.constructorФункція-конструктор, що створила об'єкт-примірник. Для примірників
Setпочатковим значенням є конструкторSet.Set.prototype.size(розмір)Повертає кількість значень, присутніх в об'єкті
Set.Set.prototype[Symbol.toStringTag]Початкове значення властивості
Symbol.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.difference()Приймає множину та повертає нову множину, що містить елементи, присутні в поточній множині, але відсутні в переданій.
Set.prototype.entries()(записи)Повертає новий об'єкт-ітератор, що містить масив із
[value, value]для кожного елемента в об'єктіSet, у порядку їх додання. Це подібно до об'єктаMap, якби ключ кожного запису був би водночас власним значенням.Set.prototype.forEach()(для кожного)Один раз викликає
callbackFnдля кожного значення, присутнього в об'єктіSet, у порядку їх додання. Якщо наданий параметрthisArg, то він використовуватиметься при кожному викликуcallbackFnяк значенняthis.Set.prototype.has()(має)Повертає булеве значення, що вказує, чи є елемент із даним значенням в об'єкті
Set.Set.prototype.intersection()Приймає множину та повертає нову множину, що вміщає елементи, присутні як у поточній множині, так і в переданій.
Set.prototype.isDisjointFrom()Приймає множину та повертає булеве значення, яке вказує на те, чи відсутні в поточної та переданої множини спільні елементи.
Set.prototype.isSubsetOf()Приймає множину та повертає булеве значення, яке вказує на те, чи всі елементи поточної множини присутні в переданій.
Set.prototype.isSupersetOf()Приймає множину та повертає булеве значення, яке вказує на те, чи всі елементи переданої множини присутні в поточній.
Set.prototype.keys()(ключі)Псевдонім для
Set.prototype.values().Set.prototype.symmetricDifference()Приймає множину та повертає нову множину, що вміщає елементи, присутні в поточній множині або в переданій, але не в них обох.
Set.prototype.union()Приймає множину та повертає нову множину, що вміщає елементи, присутні в одній з множин або в них обох.
Set.prototype.values()(значення)Повертає новий об'єкт-ітератор, що видає значення для кожного елемента в об'єкті
Set, у порядку їх додання.Set.prototype[Symbol.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, 13, 4, 4, 2, 13, 13, 4, 4, 5, 5, 6, 6, 7, 5, 32, 13, 4, 5];
console.log([...new Set(numbers)]); // [2, 13, 4, 5, 6, 7, 32]
Зв'язок із рядками
// Чутливо до регістру (множина буде містити "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" ]
Використання множини для пересвідчення щодо унікальності всіх значень у списку
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 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
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 Ні | 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 Ні | 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 Ні | 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 | 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 Ні | 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 Ні | 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 | 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 |
values
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 24 | Internet Explorer No support Ні | 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 |