JavaScript: Что такое Map и чем отличается от Object

С появлением ES2015 в JavaScript появился новый тип объектов для работы с парами ключ-значение. Называется Map. В качестве ключей и значений можно использовать любой объект. В свою очередь, стандартные объекты допускают использование только строк в качестве ключей.

Частичный перевод статьи “Storing Key-Value Pairs With JavaScript Maps” с сайта medium.com


Манипуляции

Использовать Map довольно просто. Например:

let map = new Map();
map.set('key-1', 'Value 1');
map.set('key-2', 'Any value 2');
map.set('the-key-3', 'Some value for 3');

Метод set устанавливает конкретную пару ключ-значение, но если нужно установить сразу много значений при инициализации переменной map – можно воспользоваться возможностью конструктора Map и передать все данные в него в виде вложенного массива, где первый элемент – ключ, второй – значение. Например:

const pairs = [
 ['a', 1],
 ['b', 2],
 ['c', 3]
]
let map = new Map(pairs);

Это эквивалентно трем вызовам метода set.

Чтобы получить количество пар ключ-значение содержащихся в Map можно возпользоваться свойством size:

let count = map.size; // 3

Метод get используется для получения значения по его ключу:

map.get('a'); // 1
map.get('b'); // 2
map.get('c'); // 3

Если ключ не существует то возвратится undefined:

map.get('d'); // undefined

Метод delete используется для удаления значения по ключу:

map.delete('a');

Если ключ уже существует в Map, то метод set перезапишет старое значение новым:

map.get('b'); // 2
map.set('b', 4);
map.get('b'); // 4

Чтобы удалить все пары ключ-значение из Map, используется метод clear:

map.clear();
map.size; // 0

Итерирование

Элементы Map итерируемы в порядке вставки. Например, можно использовать цикл for...of или forEach.

let map = new Map();

map.set('hi', 'Hello');
map.set('greeting', 'How are you doing?');
map.set('bye', 'Bye');

for (let [key, value] of map) {
  console.log(`${key} - ${value}`);
}

map.forEach(function(value, key) {
  console.log(`${key} => ${value}`);
});

Сравнение Map vs Object

Объекты похожи на Map. Они оба позволяют устанавливать значения по ключам, получать, удалять эти значения. Но у Map есть ряд преимуществ:

  • Ключами значений в Map могут быть любые значения, в том числе функции, объекты и примитивные типы.
  • Ключи в  Map упорядочены в порядке вставки.
  • Для получения количества элементов Map существует свойство size.
  • Map – итерируемый объект, в то время как Объект требует ручного получения списка ключей для последующей обработки.
  • Объект имеет прототип, следовательно имеет набор стандартных ключей, которые могут при неосторожности конфликтовать с вашими ключами.
  • Map более производительный в сравнении с Объектами.

Другие примеры использования Map

const myMap = new Map();

const keyObj = {},
    keyFunc = function () {},
    keyString = 'a string';

// задание значений
myMap.set(keyString, 'value associated with “a string”');
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

myMap.size; // 3

// получение значений
myMap.get(keyString);    // value associated with “a string”
myMap.get(keyObj);       // value associated with keyObj
myMap.get(keyFunc);      // value associated with keyFunc

myMap.get('a string');   // "value associated with 'a string'"
                         // потому что keyString === 'a string'
myMap.get({});           // undefined, потому что keyObj !== {} ({} — это литеральная нотация конструктора класса Object)
myMap.get(function() {}) // undefined, потому что keyFunc !== function () {}

NaN

const myMap = new Map();
myMap.set(NaN, 'not a number');

myMap.get(NaN); // not a number

const otherNaN = Number('foo');
myMap.get(otherNaN); // not a number

Работа с массивами

const kvArray = [['key1', 'value1'], ['key2', 'value2']];

// Используйте конструктор Map для преобразования двумерных массивов в Map
const myMap = new Map(kvArray);

myMap.get('key1'); // вернёт “value1”

// Используйте функцию Array.from для трансформации Map в двумерный key-value массив
console.log(Array.from(myMap)); // Выведет точно такой же массив как kvArray

// Или используйте итераторы keys или values чтобы преобразовать ключи или значения в массивы
console.log(Array.from(myMap.keys())); // Выведет ['key1', 'key2']

Клонирование и объединение

const original = new Map([
  [1, 'one']
]);

const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false.
const first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

const second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// Слияние двух Map. Взят будет последний повторившийся ключ.
// Оператор Spread по сути преобразует Map в массив
const merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
const first = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

const second = new Map([
  [1, 'uno'],
  [2, 'dos']
]);

// Слияние Map и массива. Как и при слиянии двух Map - взят будет последний повторившийся ключ.
const merged = new Map([...first, ...second, [1, 'eins']]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three