Map
Об'єкт Map (відображення) містить пари ключ-значення і запам'ятовує порядок вставки ключів у об'єкт.
Будь-які значення (і об'єкти, і примітивні значення) можуть використовуватись і як ключ, і як значення.
Спробуйте його в дії
Опис
Об'єкти Map є колекціями пар ключ-значення. Конкретний ключ в Map може зустрітись лише раз; він неповторний в межах колекції Map. Обхід об'єкта Map відбувається за його парами ключ-значення: цикл for...of на кожній ітерації поверне масив із двох елементів, [ключ, значення]. Обхід відбувається в порядку додання, котрий відповідає порядкові, в якому кожна пара ключ-значення була вставлена у відображення методом set() (тобто коли до виклику set() у відображенні не було ключа з таким само значенням).
Специфікація вимагає, щоб відображення були реалізовані "так, щоб в середньому час доступу був сублінійним відносно числа елементів колекції". Таким чином, внутрішньо вони можуть бути представлені як геш-таблиця (з доступом O(1)), як дерево пошуку (з доступом O(log(N))) або будь-яка інша структура даних, поки складність доступу краща за O(N).
Рівність ключів
Рівність значень заснована на алгоритмі sameValueZero. (Раніше використовувався алгоритм SameValue, котрий розглядав 0 і -0 як різні значення. Перевірте сумісність із браузерами.) Це означає, що значення NaN вважається рівним іншому значенню NaN (попри те, що NaN !== NaN), а всі решта значень перевіряються на рівність згідно з семантикою оператора ===.
Порівняння Object та Map
Object дуже схожий до Map: обидві структури даних дають змогу задавати значення за ключами, отримувати ці значення, видаляти ключі та визначати, чи зберігається щось за певним ключем. З таких міркувань (а ще тому, що не існувало вбудованих альтернатив), історично Object використовується як Map.
Однак, є важливі відмінності, котрі роблять Map бажаним у деяких випадках:
| `Map` | `Object` | |
|---|---|---|
| Випадкові ключі |
Новостворений Map усталено не містить ніяких ключів. Він містить лише те, що було явно покладено в нього.
|
Примітка: Цю проблему можна обійти за допомогою |
| Безпека |
Об'єкти Map безпечно використовувати з наданими користувачем ключами та значеннями.
|
Задання наданих користувачем пар ключ-значення на об'єкті |
| Типи ключів |
Ключі в Map можуть мати будь-яке значення (включно з функціями, об'єктами та будь-якими примітивними значеннями).
|
Ключі в Object повинні мати тип або String, або Symbol.
|
| Порядок ключів |
Ключі в |
Хоча ключі звичайного
Цей порядок було вперше визначено лише для власних властивостей в ECMAScript
2015; ECMAScript 2020 визначає також порядок успадкованих властивостей.
Проте зауважте, що жоден механізм не дозволяє перебрати всі властивості об'єкта; різні механізми охоплюють різні підмножини властивостей. Зокрема, ( |
Розмір |
Кількість елементів всередині Map легко отримується з його властивості size.
|
З'ясування кількості елементів усередині Object виконується в обхід, менш ефективно. Загальноприйнятий спосіб це зробити – через length масиву, поверненого з Object.keys().
|
| Перебирання |
Map — це
ітерований об'єкт, тож його елементи можна перебирати напряму.
|
Зауваження:
|
| Швидкодія |
Краще працює при ситуаціях, коли необхідно часто додавати й видаляти пари ключ-значення. |
Не оптимізований для частого вставляння і видалення пар ключ-значення. |
| Серіалізація і парсинг |
Не має нативної підтримки для серіалізації чи парсингу.
(Проте можна збудувати власну підтримку для серіалізації та розбирання об'єктів |
Нативна підтримка серіалізації з
Нативна підтримка для парсингу JSON у |
Призначення властивостей об'єкта
Призначення властивостей об'єкта так само працює і для об'єктів типу Map, і це може спричинити суттєву плутанину.
Отже, може скластися враження, що це працює таким чином:
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Однак цей спосіб присвоєння властивості ніяк не взаємодіє зі структурою даних Map. Він лише використовує особливість звичайного об'єкта. Значення 'bla' не збереглося всередині Map для запитів. Інші операції на даних зазнають невдачі:
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
Правильно використовувати Map для зберігання даних через метод set(key, value).
const contacts = new Map();
contacts.set("Яся", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Яся"); // true
contacts.get("Галина"); // undefined
contacts.set("Галина", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Яся"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Роман"); // false
contacts.delete("Яся"); // true
console.log(contacts.size); // 1
Map-подібні браузерні API
Браузерні Map-подібні об'єкти – це інтерфейси Web API, які багато в чому поводяться подібно до Map.
Як і Map, їхні записи можуть бути ітеровані в тому самому порядку, в якому були додані до об'єкта.
Також Map-подібні об'єкти та Map мають властивості та методи, що поділяють однакові назву та поведінку.
Проте, на відміну від Map, ці об'єкти дозволяють лише певні попередньо визначені типи для ключів та значень кожного запису.
Дозволені типи задані у визначенні специфікації IDL.
Наприклад, RTCStatsReport – це Map-подібний об'єкт, який повинен використовувати рядки як ключі та об'єкти як значення.
Це визначено в специфікації IDL нижче:
interface RTCStatsReport {
readonly maplike<DOMString, object>;
};
Map-подібні об'єкти – це або лише для читання, або для читання та запису (див. ключове слово readonly у специфікації IDL вище).
Map-подібні об'єкти лише для читання мають властивістьsize, а також методи:entries(),forEach(),get(),has(),keys(),values()таSymbol.iterator.- Записувані
Map-подібні об'єкти, на додачу, мають методи:clear(),delete()таset().
Ці методи та властивості мають таку ж поведінку, як рівносильні сутності в Map, окрім обмеження на типи ключів та значень.
Нижче – приклади Map-подібних браузерних об'єктів лише для читання:
Конструктор
Map()Створює новий об'єкт
Map.
Статичні властивості
Map[Symbol.species]Функція конструктора, що застосовується для створення похідних об'єктів.
Статичні методи
Map.groupBy()Групує елементи переданого ітерованого об'єкта за допомогою значень, повернених переданою функцією зворотного виклику. Результівний об'єкт
Mapвикористовує унікальні значення з перевіркової функції як ключі, котрі можуть вживатися для отримання масиву елементів кожної групи.
Властивості примірника
Ці властивості означені на Map.prototype і є спільними для всіх примірників Map.
Map.prototype.constructorФункція-конструктор, що створила об'єкт-примірник. Для примірників
Mapпочатковим значенням є конструкторMap.Map.prototype[Symbol.toStringTag]Початкове значення властивості
Symbol.toStringTag– рядок"Map". Ця властивість використовується вObject.prototype.toString().Map.prototype.sizeПовертає кількість пар ключ-значення об'єкта
Map.Map.prototype[Symbol.toStringTag]Початковим значенням властивості
Symbol.toStringTagє рядок"Map". Вона використовується вObject.prototype.toString().
Методи примірника
Map.prototype.clear()Видаляє всі пари ключ-значення з об'єкта
Map.Map.prototype.delete()Повертає
true, якщо вказаний елемент знаходився всерединіMapі був успішно видалений, або жfalse, якщо вказаний елемент не існує. Після цьогоmap.has(key)повертатимеfalse.Map.prototype.entries()Повертає новий об'єкт Iterator, що містить масив із двох елементів:
[key, value], на кожний елемент об'єктаMap, у порядку їх вставки.Map.prototype.forEach()Викликає функцію
callbackFnодин раз для кожної пари ключ-значення, наявної в об'єктіMap, в порядку вставки. Якщо в методforEachпередано параметрthisArg, його буде використано як значенняthisдля кожної функції зворотного виклику.Map.prototype.get()Повертає значення, пов'язане з
key, або жundefined, якщо такого значення немає.Map.prototype.has()Повертає булеве значення, яке відображає наявність чи відсутність якогось значення, пов'язаного з переданим ключем, в об'єкті
Map.Map.prototype.keys()Повертає новий об'єкт Iterator, що містить ключі кожного елементу об'єкта
Map, в порядку їх вставки.Map.prototype.set()Призначає значення за переданим ключем в об'єкті
Map. Повертає об'єктMap.Map.prototype.values()Повертає новий об'єкт Iterator, який містить значення кожного елементу об'єкта
Map, в порядку їх вставки.Map.prototype[Symbol.iterator]()Повертає новий об'єкт Iterator, що містить масив із двох елементів:
[key, value], на кожний елемент об'єктаMap, у порядку їх вставки.
Приклади
Застосування об'єкта Map
const myMap = new Map();
const keyString = "рядок";
const keyObj = {};
const keyFunc = function () {};
// встановлення значень
myMap.set(keyString, "значення, асоційоване з рядком");
myMap.set(keyObj, "значення, асоційоване з об'єктом");
myMap.set(keyFunc, "значення, асоційоване з функцією");
console.log(myMap.size); // 3
// отримання значень
console.log(myMap.get(keyString)); // "значення, асоційоване з рядком"
console.log(myMap.get(keyObj)); // "значення, асоційоване з об\'єктом"
console.log(myMap.get(keyFunc)); // "значення, асоційоване з функцією"
console.log(myMap.get("рядок")); // "значення, асоційоване з рядком"
// оскільки keyString === 'a string'
console.log(myMap.get({})); // undefined, оскільки keyObj !== {}
console.log(myMap.get(function () {})); // undefined, оскільки keyFunc !== function () {}
Застосування NaN як ключів для Map
Значення NaN також можна використовувати як ключ. І хоча кожне значення NaN не дорівнює саме собі (вираз NaN !== NaN — істинний), наступний приклад спрацює, тому що значення NaN неможливо розрізнити одне від одного:
const myMap = new Map();
myMap.set(NaN, "не число");
myMap.get(NaN);
// "не число"
const otherNaN = Number("foo");
myMap.get(otherNaN);
// "не число"
Перебирання об'єктів Map за допомогою for...of
Об'єкти Map можна перебирати за допомогою циклу for...of:
const myMap = new Map();
myMap.set(0, "нуль");
myMap.set(1, "один");
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = нуль
// 1 = один
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// нуль
// один
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = нуль
// 1 = один
Перебирання об'єктів Map за допомогою forEach()
Об'єкти Map можна перебирати за допомогою методу forEach():
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = нуль
// 1 = один
Зв'язок з масивами
const kvArray = [
["key1", "value1"],
["key2", "value2"],
];
// Використаємо звичайний конструктор Map, щоб перетворити двовимірний масив комбінацій ключ-значення на `Map`
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // повертає "value1"
// Використаємо Array.from(), щоб перетворити об'єкт `Map` у двовимірний масив пар ключ-значення
console.log(Array.from(myMap)); // Покаже точнісінько такий самий масив, як kvArray
// Лаконічний спосіб зробити те саме, за допомогою оператора розгортання
console.log([...myMap]);
// Або ж використовуйте ітератори keys() чи values() і перетворюйте їх у масив
console.log(Array.from(myMap.keys())); // ["key1", "key2"]
Клонування і злиття об'єктів Map
Так само як і масиви, об'єкти Map можна клонувати:
const original = new Map([[1, "one"]]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false (корисно для поверхневого порівняння)
[!NOTE] Майте на увазі, що власне дані тут клоновано не було.
Об'єкти Map можна поєднувати, зберігаючи унікальність ключів:
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]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Об'єкти Map можна також об'єднувати з іншими масивами:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Зливаємо докупи об'єкти `Map` з масивом. У разі конфлікту ключів наступний перезапише попередній.
const merged = new Map([...first, ...second, [1, "un"]]);
console.log(merged.get(1)); // un
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Специфікації
Сумісність із браузерами
| desktop | mobile | server | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Map
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
@@iterator
|
Chrome Full support 43 | Edge Full support 12 | Firefox Full support 36 | Internet Explorer No support Ні | Opera Full support 30 | Safari Full support 10 | WebView Android Full support 43 | Chrome Android Full support 43 | Firefox for Android Full support 36 | Opera Android Full support 30 | Safari on iOS Full support 10 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
@@species
|
Chrome Full support 51 | Edge Full support 13 | Firefox Full support 41 | Internet Explorer No support Ні | Opera Full support 38 | Safari Full support 10 | WebView Android Full support 51 | Chrome Android Full support 51 | Firefox for Android Full support 41 | Opera Android Full support 41 | Safari on iOS Full support 10 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.5.0 |
@@toStringTag
|
Chrome Full support 44 | Edge Full support 79 | Firefox Full support 51 | Internet Explorer No support Ні | Opera No support Ні | Safari Full support 9.1 | WebView Android Full support 44 | Chrome Android Full support 44 | Firefox for Android Full support 51 | Opera Android No support Ні | Safari on iOS Full support 9.3 | Samsung Internet Full support 4.0 | Deno Full support 1.0 | Node.js Full support 6.0.0 |
Map() constructor
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
new Map(iterable)
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
Map() without new throws
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 42 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 42 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
new Map(null)
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 37 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 37 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
clear
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 19 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 19 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
delete
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
entries
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
forEach
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 25 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 25 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
get
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
has
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
Key equality for -0 and 0
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 29 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 9 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 29 | Opera Android Full support 25 | Safari on iOS Full support 9 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 4.0.0 |
keys
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
set
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer Partial support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
size
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 19 | Internet Explorer Full support 11 | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 19 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
values
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 20 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 8 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 20 | Opera Android Full support 25 | Safari on iOS Full support 8 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |