Способи створення набору в JavaScript?


83

У Eloquent JavaScript, глава 4, набір значень створюється шляхом створення об’єкта та зберігання значень як імен властивостей, присвоєння довільних значень (наприклад, true) як значень властивостей. Щоб перевірити, чи значення вже міститься в наборі, використовується inоператор:

var set = {};

if (!'Tom' in set) { 
  set.Tom = true;
}

Це ідіоматичний JavaScript? Хіба не використовувати масив ще краще?

var set = [];

if (!'Tom' in set) { 
  set.push = 'Tom';
}

1
Як би ви очікували 'Tom' in setвиглядати масив, для якого істинно? Схоже, у вас є про щось неправильні припущення , і я намагаюся з’ясувати, про що .

10
FYI, вам потрібно дужки: if(!('Tom' in set)). В даний час це означає false in setз !'Tom' === false.
pimvdb

ES6 має набори, див. Відповідь Джона нижче
Бен Таліадорос

Відповіді:


101

Набори тепер доступні в ES2015 (він же ES6, тобто ECMAScript 6). ES6 є чинним стандартом JavaScript з червня 2015 року.

ECMAScript 6 має набір даних, який працює для довільних значень, швидко і правильно обробляє NaN. - Аксель Раушмайер , Досліджуючи ES6

Перші два приклади з книги Акселя Раушмайєра « Дослідження ES6» :

Управління окремими елементами:

> let set = new Set();
> set.add('red')

> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false

Визначення розміру набору та очищення його:

> let set = new Set();
> set.add('red')
> set.add('green')

> set.size
2
> set.clear();
> set.size
0

Я хотів би ознайомитися з Exploring ES6, якщо ви хочете дізнатись більше про набори в JavaScript. Книгу можна читати безкоштовно в Інтернеті, але якщо ви хочете підтримати автора доктора Акселя Раушмайера, ви можете придбати книгу приблизно за 30 доларів.

Якщо ви хочете використовувати набори та ES6 зараз, ви можете використовувати Babel , перетворювач ES6 на ES5 та його поліфіли.

Редагувати: станом на 6 червня 2017 року більшість основних браузерів мають повну підтримку Set у своїх останніх версіях (крім IE 11). Це означає, що вам може не знадобитися babel, якщо ви не хочете підтримувати старіші браузери. Якщо ви хочете побачити сумісність у різних браузерах, включаючи ваш поточний браузер, перевірте таблицю сумісності Kangax ES6 .

РЕДАГУВАТИ:

Просто роз'яснення щодо ініціалізації. Набори можуть приймати будь-який синхронний ітерабель у своєму конструкторі. Це означає, що вони можуть приймати не просто масиви, а й рядки та ітератори. Візьмемо для прикладу наступну ініціалізацію масиву та рядка набору:

const set1 = new Set(['a','a','b','b','c','c']);
console.log(...set1);
console.log(set1.size);
const set2 = new Set("aabbcc");
console.log(...set2);
console.log(set2.size);

Обидва виходи масиву та рядка однакові. Зверніть увагу, що ...set1це синтаксис поширення . Здається, кожен елемент ітерабельного додається по одному до набору, тому оскільки і масив, і рядок мають однакові елементи, а оскільки елементи знаходяться в однаковому порядку, набір створюється однаковим. Інша річ, на яку слід звернути увагу щодо наборів - це коли ітерація над ними порядок ітерацій слідує порядку, коли елементи були вставлені в набір. Ось приклад ітерації набору:

const set1 = new Set(['a','a','b','b','c','c']);
for(const element of set1) {
  console.log(element);
}

Оскільки ви можете використовувати будь-який ітерабель для ініціалізації набору, ви навіть можете використовувати ітератор із функції генератора . Ось два таких приклади ініціалізації ітераторів, які видають однакові результати:

// a simple generator example
function* getLetters1 () {
  yield 'a';
  yield 'a';
  yield 'b';
  yield 'b';
  yield 'c';
  yield 'c';
}

// a somewhat more commonplace generator example
// with the same output as getLetters1.
function* getLetters2 (letters, repeatTimes) {
  for(const letter of letters) {
    for(let i = 0; i < repeatTimes; ++i) { 
      yield letter;
    }
  }
}

console.log("------ getLetters1 ------");
console.log(...getLetters1());
const set3 = new Set(getLetters1());
console.log(...set3);
console.log(set3.size);

console.log("------ getLetters2 ------");
console.log(...getLetters2('abc', 2));
const set4 = new Set(getLetters2('abc', 2));
console.log(...set4);
console.log(set4.size);

Функції генератора цих прикладів можна просто записати, щоб не повторювати, але якщо функція генератора є більш складною, і якщо наступне не впливає на продуктивність занадто негативно, ви можете використовувати метод Set, щоб допомогти отримати лише значення з генератора, які не не повторюю.

Якщо ви хочете дізнатись більше про набори, не читаючи глави своєї книги доктора Раушмаєра, ви можете ознайомитись із документами MDN щодо набору . MDN також має більше прикладів ітерації по набору , такі як використання forEachі використання .keys, .valuesі .entriesметоди. MDN також має такі приклади, як об'єднання множин, перетин множин, різниця множин, симетрична різниця множин та перевірка набору наборів. Сподіваємось, більшість цих операцій стануть доступними в JavaScript без необхідності створювати власні функції, що їх підтримують. Насправді є така пропозиція TC39 щодо нових методів Set, яка, сподіваємось, повинна додати наступні методи до Set у JavaScript в якийсь майбутній момент часу, якщо пропозиція досягне 4-ї стадії:

  • Set.prototype.intersection (iterable) - метод створює новий екземпляр Set за допомогою операції перетину множин.
  • Set.prototype.union (iterable) - метод створює новий екземпляр Set за допомогою операції об'єднання наборів.
  • Set.prototype.difference (iterable) - метод створює новий Set без елементів, присутніх в iterable.
  • Set.prototype.symmetricDifference (iterable) - повертає набір елементів, знайдених лише в цьому або в ітерабельному.
  • Set.prototype.isSubsetOf (ітерабельний)
  • Set.prototype.isDisjointFrom (ітерабельний)
  • Set.prototype.isSupersetOf (ітерабельний)

Також самі Set є ітерабельними, тому ви можете ініціалізувати набори іншими наборами, щоб зробити копію або зробити щось на зразок нового Set ([... setA, ... setB]) для операції об'єднання.
Джон

1
@LanceKind Я додав додаткову інформацію, включаючи показ того, що можна ініціалізувати набори будь-якими ітерабельними, а не лише масивами.
Джон

32

Я використовую об'єкти dict як набори. Це працює зі рядками та числами, але, гадаю, це може спричинити проблеми, якщо ви хочете мати набір об'єктів, що використовують спеціальні оператори рівності та порівняння:

Створення набору:

var example_set = 
{
    'a':true,
    'b':true,
    'c':true
}

Тестування на включення в набір

if( example_set['a'] ){
    alert('"a" is in set');
}

Додавання елемента до набору

example_set['d'] = true;

Видалення елемента з набору

delete example_set['a'];


1
Код, над яким я працював, використовував стару версію двигуна, яка не мала підтримки Set. Це допомогло.
Abhijith Madhav

16

Набори не дозволяють повторюваних записів і зазвичай не гарантують заздалегідь визначене замовлення. Масиви виконують обидва ці дії, тим самим порушуючи, що означає бути набором (якщо ви не зробите додаткових перевірок).


2
Масив не фільтрує повторювані записи ... наприклад, arr.push ({id: 1, name: "Jake"}), як NSSet в Objective-C :)
iTux

Якщо взяти мот-а-мот, ні. Але ви можете просто використовувати ідентифікатор як ключ масиву (карти). arr[id] = {"name": "Jake"};
Буффало,

11

Перший спосіб - це ідіоматичний JavaScript.

Кожного разу, коли ви хочете зберегти пару ключ / значення, ви повинні використовувати об'єкт JavaScript. Що стосується масивів, то існує кілька проблем:

  1. Індекс є числовим значенням.

  2. Непростий спосіб перевірити, чи є значення в масиві без перегляду.

  3. Набір не дозволяє дублікати. Масив робить.


Що найкраще призначити вартості майна?
helpermethod

1
Можна видалити повторювані елементи з масивів JavaScript. stackoverflow.com/a/12166248/975097
Андерсон Грін

9

Якщо ви хочете створити набір з масиву, просто виконайте:

let arr = [1, 1, 2, 1, 3];
let mySet = new Set(arr); // Set { 1, 2, 3 }

Це синтаксис цукру, який мені дуже сподобався, програмуючи на Python, так радий, що ES6 нарешті дозволив зробити те саме.

ПРИМІТКА: тоді я розумію, що сказане мною безпосередньо не відповіло на ваше запитання. Причина, по якій у вас є цей "хак" у ES5, полягає в тому, що час пошуку об'єкта за ключами значно швидший (O (1)), ніж у масиві (O (n)). У критично важливих для продуктивності додатках ви можете пожертвувати цією частиною читабельності або інтуїції для кращої продуктивності.

Але привіт, ласкаво просимо до 2017 року, де зараз ви можете використовувати належний набір у всіх основних сучасних браузерах!


6

Встановлюється в ES6/ ES2015:

ES6/ ES2015тепер має вбудовані набори. Набір - це структура даних, яка дозволяє зберігати унікальні значення будь-якого типу, будь то примітивні значення або посилання на об’єкти. Набір можна оголосити за допомогою ES6вбудованого конструктора набору наступним чином:

const set = new Set([1, 2, 3, 4, 5]);

При створенні набору за допомогою конструктора Set наш новостворений об'єкт набору успадковується від Set.prototype. Це має всілякі допоміжні методи та властивості. Це дозволяє легко робити наступні дії:

Приклад:

const set = new Set([1, 2, 3, 4, 5]);

// checkout the size of the set
console.log('size is: ' + set.size);

// has method returns a boolean, true if the item is in the set
console.log(set.has(1));

// add a number
set.add(6);

// delete a number
set.delete(1);

// iterate over each element using a callback
set.forEach((el) => {
  console.log(el);
});

// remove all the entries from the set
set.clear();

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

Зараз усі основні браузери повністю підтримують набори, крім IE, де відсутні деякі функції. Точне посилання див. У документах mdn .


@Velojet: Гаразд, справедливо.
kjhughes

3

Існує дві проблеми з використанням оголених об'єктів javascript для емуляції наборів: по-перше, об'єкт може мати успадковану властивість, яка вкручує оператор "in", а по-друге, ви можете зберігати лише скалярні значення таким чином, створення набору об'єктів не є можливо. Отже, реалістична реалізація наборів повинна передбачати методи, addа containsне просто inприсвоєння та присвоєння майна.


@kojiro: об'єкти перетворюються на рядки, коли використовуються як ключі:set={};set[{x:1}]=123;alert(set[{z:99}])
georg

3

Ви можете спробувати Buckets , це бібліотека структури даних javascript і має все необхідне для маніпулювання наборами.


це забезпечує метод addAll (масив)?
jorrebor

1

Основне створення та використання об'єкта Set 🔷

let mySet = new Set()

mySet.add(2)         // Set {2}
mySet.add(7)         // Set {2, 7}
mySet.add(7)         // Set {2, 7}
mySet.add('my text') // Set {2, 7, 'my text'}
let myObj = { a: 1, b: 2 }
mySet.add(myObj)     // Set {2, 7, 'my text', {...}}
mySet.has(2)         // true
mySet.has(myObj)     // true
mySet.size           // 4

Ітерація

for (let item of mySet) console.log(item)  // 2, 7, 'my text', {a:1, b:2}
mySet.forEach(value => console.log(value)) // 2, 7, 'my text', {a:1, b:2}

Перетворити на масив

var myArr = Array.from(mySet)             // [2, 7, 'my text', {a:1, b:2}]

Найвиразнішою функцією, яку пропонує Set, є те, що кожне значення об’єкта Set має бути унікальним. Таким чином, ви не можете додавати повторювані значення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.