JavaScript: Как использовать Коллекции — Map и Set

Источник: «How to Use JavaScript Collections – Map and Set»
В JavaScript объекты используются для хранения нескольких значений в виде сложной структуры данных.

object создаётся с помощью фигурных скобок {…} и списка свойств. Свойство — пара ключ-значение, где ключ должен быть строкой, а значение может быть любого типа.

С другой стороны, array — упорядоченная коллекция, содержащая данные любого типа. В JavaScript массивы создаются с помощью квадратных скобок […] и допускают дублирование элементов.

До ES6 (ECMAScript 2015) объекты и массивы JavaScript были наиболее важными структурами данных для обработки коллекций данных. Помимо этого, у сообщества разработчиков не было большого выбора. Тем не менее комбинация объектов и массивов могла обрабатывать данные во многих сценариях.

Однако было несколько недостатков:

С введением ES6 мы получили две новые структуры данных, устраняющие вышеупомянутые недостатки: Map и Set. В этой статье мы внимательно рассмотрим обе и поймём как их использовать в разных ситуациях.

Map в JavaScript

Map — набор пар ключ-значение, где ключ может быть любого типа. Map запоминает первоначальный порядок добавления элементов, это означает, что данные могут быть извлечены в том же порядке в каком они были вставлены.

Другими словами, Map имеет характеристики как объекта, так и массива:

Как создать и инициализировать Map в JavaScript

Новый Map можно создать так:

const map = new Map();

Который вернёт пустой Map:

Map(0) {}

Другой способ создания Map — с начальными значениями. Вот как создать Map с тремя парами ключ-значение:

const freeCodeCampBlog = new Map([
['name', 'freeCodeCamp'],
['type', 'blog'],
['writer', 'Tapas Adhikary'],
]);

Который возвращает Map с тремя элементами:

Map(3) {"name" => "freeCodeCamp", "type" => "blog", "writer" => "Tapas Adhikary"}

Как добавить значения в Map

Чтобы добавить значение в Map используйте метод set(ключ, значение).

Метод set(ключ, значение) принимает два параметра, ключ и значение, где ключ и значение могут быть любого типа, примитивами (boolean, string, number и т.д.) или объектами:

// Создаём map
const map = new Map();

// добавляем значения в map
map.set('name', 'freeCodeCamp');
map.set('type', 'blog');
map.set('writer', 'Tapas Adhikary');

// Вывод:
// Map(3) {"name" => "freeCodeCamp", "type" => "blog", "writer" => "Tapas Adhikary"}

Обратите внимание: если вы используете один и тот же ключ для добавления значения несколько раз, оно всегда будет заменять предыдущее значение:

// Добавляем другого автора
map.set('writer', 'Someone else!');

Вывод будет:

Map(3) {"name" => "freeCodeCamp", "type" => "blog", "writer" => "Someone else!"}

Как получить значение из Map

Для получения значения из Map используйте метод get(ключ):

map.get('name'); // вернёт freeCodeCamp

Всё о ключах Map

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

// Создаём Map
const funMap = new Map();

funMap.set(360, 'My House Number'); // number в качестве ключа
funMap.set(true, 'I write blogs!'); // boolean в качестве ключа

let obj = {'name': 'tapas'}
funMap.set(obj, true); // object в качестве ключа

console.log(funMap);

Вот результат:

Map(3)
{
360 => "My House Number",
true => "I write blogs!",
{} => true
}

Обычный объект JavaScript всегда обрабатывает ключ как строку. Даже когда вы ему передаёте примитив или объект, он внутренне преобразует ключ в строку:

// Создаём пустой объект
const funObj = {};

// добавляем свойство. Заметьте, что ключ передаётся как число.
funObj[360] = 'My House Number';

// Возвращает true, потому что число 360 было внутренне преобразовано в строку `360`!
console.log(funObj[360] === funObj['360']);

Свойства и методы Map

JavaScript Map имеет встроенный свойства и методы, упрощающие его использование. Вот некоторые основные:

MapIterator: keys(), values() и entries()

Методы keys(), values() и entries() возвращают MapIterator, что превосходно, потому что вы можете использовать цикл for-of или forEach непосредственно на нём.

Сначала создадим простой Map:

const ageMap = new Map([
['Jack', 20],
['Alan', 34],
['Bill', 10],
['Sam', 9]
]);

Как перебрать все элементы Map

Вы можете использовать цикл forEach или for-of для перебора Map:

// c forEach
ageMap.forEach((value, key) => {
console.log(`${key} is ${value} years old!`);
});

// c for-of
for(const [key, value] of ageMap) {
console.log(`${key} is ${value} years old!`);
}

В обоих случаях результат будет одинаковым:

Jack is 20 years old!
Alan is 34 years old!
Bill is 10 years old!
Sam is 9 years old!

Как конвертировать Объект в Map

Вы можете столкнуться с ситуацией, когда нужно преобразовать object в Map структуру. Можно использовать метод entries Object и сделать так:

const address = {
'Tapas': 'Bangalore',
'James': 'Huston',
'Selva': 'Srilanka'
};

const addressMap = new Map(Object.entries(address));

Как конвертировать Map в Объект

Конвертировать Map в объект можно с помощью метода fromEntries:

Object.fromEntries(map)

Как конвертировать Map в Массив

Есть несколько способов преобразования Map в массив:

Map или Объект: Когда использовать?

У Map есть характеристики, как object, так и array. Однако Map больше похож на object, чем на array из-за природы хранения данных в формате ключ-значение.

На этом сходство с объектами заканчивается. Как вы видели, Map во многом отличается. Итак, какой из этих типов и когда следует использовать? Как вы считаете?

Когда использовать Map:

Когда использовать object:

Set в JavaScript

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

Set в JavaScript ведёт себя так же, как математические множества.

Как создать и инициализировать Set

Новый Set можно создать следующим образом:

const set = new Set();
console.log(set);

// Вывод:
Set(0) {}

Вот так можно создать Set с начальными значениями:

const fruitSet = new Set(['🍉', '🍎', '🍈', '🍏']);
console.log(fruitSet);

// Вывод:
Set(4) {"🍉", "🍎", "🍈", "🍏"}

Методы и Свойства Set

В Set есть методы для добавления элемента, удаления одного из элементов, проверка наличия элемента и полная очистка (удаление всех элементов):

Как перебрать все элементы Set

В Set есть метод values(), возвращающий SetIterator для получения всех значений:

// Создаём Set
const houseNos = new Set([360, 567, 101]);

// Получаем SetIterator используя метод `values()`
console.log(houseNos.values());

// Вывод:
SetIterator {360, 567, 101}

Можно использовать цикл forEach или for-of для получения значений.

Интересно, что JavaScript пытается сделать Set совместимым с Map. Вот почему у него есть два похожих метода keys() и entry(), как в Map.

Поскольку в Set нет ключей, метод keys() возвращает SetIterator для получения значений:

console.log(houseNos.keys());

// Вывод:
// SetIterator {360, 567, 101}

Метод entry() возвращает итератор для извлечения пар ключ-значение. Опять же в Set нет ключей, поэтому entry() возвращает SetIterator для получения пар значение-значение:

console.log(houseNos.entries());

// Вывод:
// SetIterator {360 => 360, 567 => 567, 101 => 101}

Мы можем вывести все значения Set используя циклы forEach и for-of:

// forEach
houseNos.forEach((value) => {
console.log(value);
});


// for-of
for(const value of houseNos) {
console.log(value);
}

Вывод в обоих случаях одинаковый:

360
567
101

Set и Массивы

Массив, как и Set, позволяет добавлять и удалять элементы. Но Set совершенно иной тип и не предназначен для замены массивов.

Основное отличие в том, что массивы могут иметь повторяющиеся элементы. Кроме того, некоторые операции Set, такие, как delete() выполняются быстрее, чем операции с массивами, такие как shift() или splice().

Думайте о Set, как о расширении обычного массива, только с большим количеством мышц. Структура данных Set не является заменой массива, оба могут решать интересные задачи.

Как конвертировать Set в Массив

Set очень просто конвертируется в массив:

const arr = [...houseNos];
console.log(arr);

// Вывод:
// (3) [360, 567, 101]

Получение уникальных значений из массива с помощью Set

Создание Set — простой способ удаления повторяющихся элементов из массива:

// Создадим массив mixedFruit с несколькими дубликатами фруктов
const mixedFruit = ['🍉', '🍎', '🍉', '🍈', '🍏', '🍎', '🍈'];

// Создаём из массива уникальный набор
const mixedFruitSet = new Set(mixedFruit);

console.log(mixedFruitSet);

// Вывод
// Set(4) {'🍉', '🍎', '🍈', '🍏'}

Set и Объект

Set может содержать элементы любого типа, даже объект:

// Создаём объект person
const person = {
'name': 'Alex',
'age': 32
};

// Создаём Set и добавляем в него объект
const pSet = new Set();
pSet.add(person);
console.log(pSet);

// Вывод:
// Set(1)
// [[Entries]]
// 0:
// value: {name: 'Alex', age: 32}
// size: 1
// [[Prototype]]: Set

Нет ничего удивительного — Set содержит один элемент, являющийся объектом.

Изменим свойство объекта и снова добавим его в Set:

// Изменим имя
person.name = 'Bob';

// Добавим объект person в Set снова
pSet.add(person);
console.log(pSet);

// Вывод:
//Set(1)
// [[Entries]]
// 0:
// value: {name: 'Bob', age: 32}
// size: 1
// [[Prototype]]: Set

Set — набор уникальных элементов. Изменив свойство объекта, мы не изменили сам объект. Следовательно, Set не допустит дублирования элементов.

Set — отличная структура данных, которую можно использовать в дополнение к массивам. Однако у него нет большого преимущества пред обычными массивами.

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

Дополнительные материалы

Предыдущая Статья

JavaScript: Более безопасное чтение и запись URL

Следующая Статья

PHP: Абстрактная Фабрика