Все о заданной структуре данных в 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