Все о заданной структуре данных в JavaScript.
Есть много ситуаций, когда вам нужно сравнить несколько списков и извлечь элементы, которые у них есть или не являются общими, доступны только в одном и т. Д. Наборы позволяют делать только это и многое другое. В частности, набор Javascript - очень особенный и мощный, но в нем все еще отсутствуют важные вещи, которые предлагают другие языки.
Видеоверсия этой статьи
Этот пост представляет собой улучшенную и более подробную версию статьи из серии Set Data Structure Series на Youtube, которую вы можете проверить, предпочитаете ли вы видео.
Что такое набор?
Set - это набор уникальных элементов с ключами, хранящихся в произвольном порядке. В отличие от других типов коллекций, таких как Стек, Очередь и Массив, набор используется при сравнении списков и для проверки того, существует ли элемент в наборе или нет. Правильно сказать, что Set хранит пары ключ-ключ-значение, потому что вы используете элемент, чтобы проверить, есть ли он в наборе.
Set также является абстрактным типом данных, что означает, что он определяется своим поведением во многом подобно структурам данных Stack и Queue. Из-за своей природы ключ-ключ Set во многом связан с Map, чем что-либо еще, и вы даже можете реализовать Set, используя его.
Набор Javascript
Набор Javascript очень прост и прост. Ему не хватает общих возможностей работы с наборами, которые обычно предлагают другие языки. Он также использует уникальный алгоритм для проверки идентичности элементов по сравнению со строгой проверкой тройного равенства (===).
Это означает, что сохранение в наборе «undefined», «null» и «NaN» гарантирует, что они существуют только один раз, даже если «Nan! == NaN ». В чем он проявляется, так это в хранении типов объектов, равенство которых сложно проверить.
const set = new Set([null, undefined, null, NaN, NaN, null]); console.log([...set]); // [null, undefined, NaN]
Вы вставляете с помощью « добавить » и удаляете с помощью delete »для отдельного элемента или всего набора с помощью «clear » Метод. Это Iterable и поставляется с итераторами для значений и записей, поскольку ключи и значения одинаковы. Отсюда тот факт, что его часто описывают как набор пар "ключ-ключ". Он также предоставляет метод forEach, такой как Array и Map, как более быстрый способ итерации его элементов.
const set = new Set([78, 20, 44]); set.add(12); set.delete(78); set.clear();
Как упоминалось ранее, Set используется для сравнения и проверки элементов, но единственный метод, который позволяет вам что-то проверять, - это метод has, который для данного элемента возвращает true или false. есть ли в наборе или нет.
set.has(12); // true set.has(21); // false
Для выполнения более сложных проверок вы должны добавить новые методы, которые позволят вам проводить мощные сравнения и красиво работать со списковыми данными, которые я покажу вам позже.
Установить против массива
Set сильно отличается от Array, и причина, по которой они часто сравниваются друг с другом в Javascript, заключается в том, что Array часто использовался для того, что делает Set.
Массивы хранят элементы в порядке индекса и позволяют быстро читать и записывать элементы, если вы знаете их индекс. Set позволяет делать то же самое, пока у вас есть предмет. Это показывает, что Array - это индексированная коллекция элементов, а Set - это коллекция элементов на основе ключей.
Массив предназначен для использования, когда вы хотите хранить элементы в определенном порядке для доступа и управления. В наборе элементы уникальны, но могут повторяться внутри массива. Set часто используется для очистки повторения элементов массива, но он не предназначен для использования в вещах, для которых предназначен Array.
const array = [23, 41, 12, 41, 67, 23]; const noRepeatArray = Array.from(new Set(array)); // becomes [23, 41, 12, 67] learn more about Array
Set не работает и не предназначен для замены массива. Он просто решает очень конкретную проблему сравнения списков и проверок элементов, для которой Array не годится без обширного дополнительного кода для обработки.
const array = [12, 45]; const set = new Set([12, 45]); // extra code needed to ensure uniqueness if(!array.includes(12)) { array.push(12); } // does nothing since it already exists set.add(12); // slower: checks every item if necessary array.includes(45); // faster: since it checks the key set.has(45);
Когда использовать Set?
Вы используете Set, когда все, что вам нужно, это выполнить сравнение и проверку определенного списка. Допустим, у вас есть списки A и B, и вы хотите узнать, совпадают ли они. Какой список А есть у того, чего нет у Б, или что у них общего? Кроме того, предметы из набора уникальны, и это отличное свойство для использования.
Вы используете его, чтобы сохранить список уникальных элементов для последующего повторения и выполнения действий, чтобы вам не приходилось беспокоиться о том, что в списке есть повторяющиеся элементы. Вы используете его, чтобы, имея другой примерный список, вы могли выяснить, чем они отличаются или совпадают друг с другом, чтобы принять определенные решения. Вы используете его, чтобы позже изменить его на массив, чтобы выполнять больше операций, подобных массиву, и наоборот.
Установить операции
В математике всякий раз, когда вы говорите о множествах, вы можете выполнять операции. По сути, Set - это компьютерная реализация математического конечного множества.
Чтобы правильно показать вам операции Set в коде, позвольте расширить набор Javascript, чтобы унаследовать его свойства и методы и предоставить ему дополнительные необходимые методы. Для приведенного ниже кода нам нужен только один метод, который проверяет, предоставлен ли действительный набор. В этом примере допустимый набор должен быть экземпляром объекта Set, а не пустым.
class SetExtended extends Set { #isValidSet = (set) => { return set && set instanceof Set && set.size > 0; }; }
- Объединение:
Операция объединения объединяет несколько наборов и возвращает результат объединения. Для реализации кода ниже мы возвращаем новый набор, распределяя текущий и заданный набор в массиве для его создания.
union(set) { if (!this.#isValidSet(set)) return new SetExtended(); return new SetExtended([...this, ...set]); }
- Пересечение:
Операция перехвата предоставляет нам новый набор, содержащий только общие элементы. В приведенном ниже примере рассматривается меньший набор (избегает ненужных проверок) и проверяется, существует ли элемент в большом, и добавляется его в набор пересечений, а затем возвращается в конце.
intersection(set) { const intersectionSet = new SetExtended(); if (!this.#isValidSet(set)) return intersectionSet; const [smallerSet, biggerSet] = set.size <= this.size ? [set, this] : [this, set]; smallerSet.forEach((item) => { if (biggerSet.has(item)) intersectionSet.add(item); }); return intersectionSet; }
- Различие:
Операция разницы возвращает новый набор, содержащий только элементы, которые не имеют общего с другим набором. Это также известно как вычитание. Приведенная ниже реализация проходит по текущему набору и собирает элементы, которых нет в другом, в набор различий.
difference(set) { if (!this.#isValidSet(set)) return new SetExtended(); const differenceSet = new SetExtended(); this.forEach((item) => { if (!set.has(item)) differenceSet.add(item); }); return differenceSet; }
- Разница пересечения:
Операция разницы пересечения противоположна пересечению. Он также известен как Эксклюзивное ИЛИ. Он возвращает новый набор, содержащий все элементы, которые у них обоих нет, и в реализации ниже мы просто создаем новый набор с их различиями.
intersectionDifference(set) { if (!this.#isValidSet(set)) return new SetExtended(); return new SetExtended([ ...this.difference(set), ...set.difference(this), ]); }
- Подмножество:
Набор называется подмножеством, если все его элементы содержатся в другом. Приведенная ниже реализация сначала проверяет размер, потому что набор не может быть подмножеством другого, если он больше, а затем для каждого элемента он проверяет, существует ли он в другом.
isSubsetOf(set) { if (!this.#isValidSet(set)) return false; return this.size <= set.size && [...this].every(item => set.has(item)) }
- Расширенный набор:
Расширенный набор - это противоположность подмножества. Набор является расширенным, если он содержит все элементы другого набора меньшего или равного размера.
isSupersetOf(set) { if (!this.#isValidSet(set)) return false; return this.size >= set.size && [...set].every(item => this.has(item)) }
Вы не ограничены этими операциями и можете включать больше, что решает тип вашей потребности. В общем, эти проверки очень просты в реализации, и вы всегда можете использовать тот факт, что Set и Array могут быть преобразованы назад и вперед, чтобы воспользоваться преимуществами мощных методов массива.
Исходный код: Проверьте этот полный код на Github
Статический набор
Статический набор - это набор, который всегда содержит элементы, которыми он был инициализирован. Вы не можете добавлять, удалять или очищать его элементы. Набор Javascript не является статическим и всегда будет предоставлять методы, которые изменяют набор после его создания. Чтобы получить статический набор, мы должны переопределить это поведение так же, как мы его расширили.
Самый простой способ - расширить его и переопределить методы, которые его изменяют. Таким образом, вы не сможете изменить его позже.
class StaticSet extends SetExtended { constructor(items) { super(items); this.add = undefined; this.delete = undefined; this.clear = undefined; } }
Заключение
В общем, если вы часто проверяете наличие элементов в списках и фильтруете элементы для определенной части списка, это явный знак, что вам следует изучить Наборы. Набор Javascript очень мощный, с ним легко работать, и, вероятно, он очень недооценен.
Посетите блог До точки с запятой, чтобы увидеть больше подобных статей о структуре данных. Также ознакомьтесь с статьей о структуре данных массива для получения более подробной информации.
Канал YouTube: До точки с запятой
Веб-сайт: beforesemicolon.com
Больше контента на plainenglish.io