Способы создания набора на 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 02

У ES6 есть наборы, см. Ответ Джона ниже
Бен Талиадорос

Ответы:


101

Наборы теперь доступны в ES2015 (он же ES6, то есть ECMAScript 6). ES6 является текущим стандартом для JavaScript с июня 2015 года.

ECMAScript 6 имеет структуру данных Set, которая работает для произвольных значений, работает быстро и правильно обрабатывает NaN. - Аксель Раушмайер , Исследование ES6

Первые два примера из книги Акселя Раушмайера Exploring 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

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

Если вы хотите использовать Sets и 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 на Set . В 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 без элементов, присутствующих в итерабельном.
  • Set.prototype.symmetricDifference (iterable) - возвращает набор элементов, найденных только в этом или в итерабельном.
  • Set.prototype.isSubsetOf (повторяемый)
  • Set.prototype.isDisjointFrom (повторяемый)
  • Set.prototype.isSupersetOf (повторяемый)

Кроме того, сами наборы Set являются итеративными, поэтому вы можете инициализировать наборы с другими наборами, чтобы сделать копию или сделать что-то вроде new 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"};
Buffalo

11

Первый способ - идиоматический JavaScript.

Каждый раз, когда вы хотите сохранить пару ключ / значение, вы должны использовать объект JavaScript. Что касается массивов, тут несколько проблем:

  1. Индекс - это числовое значение.

  2. Нет простого способа проверить, есть ли значение в массиве, без цикла.

  3. В наборе не допускаются дубликаты. Массив делает.


Что лучше всего присвоить стоимости собственности?
helpermethod 02

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 год, где теперь вы можете использовать правильный Set во всех основных современных браузерах!


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 07

3

Есть две проблемы с использованием голых объектов javascript для имитации наборов: во-первых, объект может иметь унаследованное свойство, которое будет включать оператор "in", и, во-вторых, вы можете хранить только скалярные значения таким образом, а создание набора объектов не возможно. Следовательно, реалистичная реализация Sets должна предоставлять методы, addа containsне простые назначения inи свойства.


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

3

Вы можете попробовать Buckets , это библиотека структуры данных javascript, в которой есть все необходимое для управления наборами.


предоставляет ли он метод addAll (массив)?
jorrebor 08

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.