JavaScript: использование коллекций (Sets) и сравнение с массивами

Частичные перевод статьи «How to make your code faster using JavaScript Sets» с сайта medium.com


Я уверен, что большинство разработчиков привыкли использовать базовые типы в JavaScript: number, string, object, array, boolean.

Для большинства случаев этого вполне достаточно. Но если вы хотите сделать вам код максимально быстрым и масштабируемым — базовых типов может не хватить.

В этой статье мы поговорим как JavaScript Sets могут сделать ваш код быстрее, особенно если растет кодовая база. Есть много общего между Коллекциями и Массивами. Но использование коллекций часто придает скорости, чего невозможно достичь с Массивами.

В чем разница

Самое важное отличие Коллекций от Массивов в том, что массив это индексированная коллекция. Иными словами элементы массива отсортированы по их индексам.

С другой же стороны, коллекции не имеют индексов. Их элементы итерируемы в порядке их добавления и обязаны быть уникальными.

В чем преимущества

В прямом сравнении коллекции имеют несколько преимуществ на массивами, особенно если говорить о скорости выполнения:

  • Поиск элементов. indexOf или includes работают сравнительно медленно;
  • Удаление элементов. Из коллекций элементы удаляются по значению. Это на много быстрее, чем использование splice, например;
  • Вставка элементов. Быстрее добавить элемент в коллекцию, чем делать это в массив через push, unshift и тп.;
  • Удаление дубликатов. Тут даже делать ничего не надо — все элементы уже уникальны.

Есть еще одна особенность, которая может пригодиться (но это не точно). В коллекции можно хранить и искать значение NaN.

Сложность выполнения

Методы для поиска элементов массива имеют линейную сложность выполнения O(N). Это значит сложность растет по мере роста количества элементов.

У коллекций же основные методы имеют сложность O(1) — размер коллекции никак не влияет на скорость выполнения.

Примеры

Создание объекта коллекции

var set = new Set();

Операции с элементами

set.add(1); // Set { 1 }
set.add(2); // Set { 1, 2 }
set.add(1); // Set { 1, 2 }
set.add({a: 1}) // Set { 1, 2, {a: 1} }

// Литералы объектов уникальны. Удалить по значению нельзя.
set.add({a: 1}); // Set { 1, 2, {a: 1}, {a: 1} }
set.delete({a: 1}); // false
console.log(set); // Set { 1, 2, {a: 1}, {a: 1} }

// Удалять нужно по ссылке
var o = {a: 1};
set.add(o); // Set { 1, 2, {a: 1}, {a: 1}, {a: 1} }
set.delete(o); // true
console.log(set); // Set { 1, 2, {a: 1}, {a: 1} }

set.has(1); // true
set.has(3); // false
set.has({a: 1}); // false

set.size; // 4

Другие примеры работы с коллекциями

// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet) console.log(item);

// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet.keys()) console.log(item);
 
// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2}
for (let item of mySet.values()) console.log(item);

// выведет элементы по порядку: 1, "some text", {"a": 1, "b": 2} 
//(key и value в данном случае одинаковы)
for (let [key, value] of mySet.entries()) console.log(key);

// преобразует Set в Array
var myArr = Array.from(mySet); // [1, "some text", {"a": 1, "b": 2}]

// следующее будет работать при запуске с HTML документом
mySet.add(document.body);
mySet.has(document.querySelector("body")); // true

// преобразования из Array в Set и обратно
mySet2 = new Set([1,2,3,4]);
mySet2.size; // 4
[...mySet2]; // [1,2,3,4]

// пересечение можно представить следующим образом  
var intersection = new Set([...set1].filter(x => set2.has(x)));

// разность можно представить следующим образом
var difference = new Set([...set1].filter(x => !set2.has(x))); 

// Обход элементов set при помощи forEach
mySet.forEach(function(value) {
  console.log(value);
});

// 1
// 2
// 3
// 4

var myArray = ["value1", "value2", "value3"];

// Используйте конструктор Set для преобразования Array в Set
var mySet = new Set(myArray);

mySet.has("value1"); // вернёт true

// Используйте spread оператор для преобразования Set в Array
console.log([...mySet]); // Отобразит тот же массив, что и myArray
var text = 'India';

var mySet = new Set(text); // Set ['I', 'n', 'd', 'i', 'a']
mySet.size; // 5

На этом все. Если вы еще не работали с коллекциями — надеюсь я продемонстрировал насколько они могут быть полезны.