`Map` объект

В этой статье объясняется объект Map.

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

YouTube Video

Map объект

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

Основы Map

Сначала давайте посмотрим, как создать Map и выполнять базовые операции.

Следующий код является минимальным примером, который создаёт пустую карту, добавляет ключи и получает значения.

1// Create an empty Map and add key-value pairs
2const m = new Map();
3m.set('a', 1);
4m.set('b', 2);
5
6console.log(m.get('a')); // 1
7console.log(m.size);     // 2
  • В этом коде вы можете добавлять элементы с помощью set, получать значения с помощью get и проверять количество элементов с помощью size.
  • Map сохраняет порядок вставки, что делает его удобным для обработки, зависящей от порядка.

Поведение методов set, get, has и delete

Ниже приведены примеры типичных операций чтения, записи, проверки наличия и удаления.

С этим кодом вы можете проверить возвращаемые значения и эффекты каждого метода.

 1// Demonstrate set, get, has, and delete
 2const m2 = new Map();
 3m2.set('x', 10);
 4console.log(m2.has('x'));  // true
 5console.log(m2.get('y'));  // undefined
 6
 7m2.delete('x');
 8console.log(m2.has('x'));  // false
 9
10// set returns the map itself, allowing method chaining
11m2.set('a', 1).set('b', 2);
12console.log(m2);  // Map { 'a' => 1, 'b' => 2 }
  • get возвращает undefined, если ключ не существует. has проверяет наличие ключа, а delete удаляет ключ.
  • Кроме того, так как set возвращает сам объект map, возможна цепочка вызовов методов.

В качестве ключа может использоваться любой тип (использование объектов в качестве ключей)

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

В следующем примере показано, как ассоциировать значения в Map, используя объекты в качестве ключей.

 1// Use objects as keys in a Map
 2const keyObj = { id: 1 };
 3const keyFunc = () => {};
 4const objMap = new Map();
 5
 6// Another object with the same content but a different reference
 7const anotherKeyObj = { id: 1 };
 8
 9objMap.set(keyObj, 'objectValue');
10objMap.set(keyFunc, 'functionValue');
11objMap.set(anotherKeyObj, 'anotherValue');
12
13console.log(objMap.get(keyObj));         // 'objectValue'
14console.log(objMap.get(keyFunc));        // 'functionValue'
15console.log(objMap.get(anotherKeyObj));  // 'anotherValue'
  • При использовании объектов в качестве ключей важно, чтобы это были одни и те же ссылки. Даже если их содержимое одинаково, объекты с разными ссылками считаются разными ключами.

Итерация (перебор)

Map сохраняет порядок вставки, поэтому часто используется перебор.

Ниже мы покажем, как использовать for...of, forEach, а также методы keys(), values() и entries().

 1// Iterating a Map with for...of and forEach
 2const iterMap = new Map([['a', 1], ['b', 2], ['c', 3]]);
 3
 4// for...of over entries (default)
 5for (const [key, value] of iterMap) {
 6  console.log(key, value);
 7}
 8
 9// forEach callback
10iterMap.forEach((value, key) => {
11  console.log(key, value);
12});
13
14// keys() and values()
15console.log([...iterMap.keys()]);   // ['a','b','c']
16console.log([...iterMap.values()]); // [1,2,3]
  • entries() возвращает массив пар [ключ, значение], который можно превратить в массив с помощью оператора расширения. Обратите внимание, что функция обратного вызова для forEach получает аргументы в порядке value, key.

Преобразование между Map и Object

Вы можете преобразовать существующий объект в Map или Map в обычный объект или массив.

 1// Convert between Map and Object / Array
 2const obj = { a: 1, b: 2 };
 3const mapFromObj = new Map(Object.entries(obj)); // Object -> Map
 4console.log(mapFromObj.get('a')); // 1
 5
 6const objFromMap = Object.fromEntries(mapFromObj); // Map -> Object
 7console.log(objFromMap); // { a: 1, b: 2 }
 8
 9const arrayFromMap = [...mapFromObj]; // Map -> Array of [key, value]
10console.log(arrayFromMap); // [['a',1], ['b',2]]
  • Использование Object.entries и Object.fromEntries упрощает преобразование. Однако, поскольку ключи объекта ограничены строками или символами, при обратном преобразовании в объект нестроковые ключи теряются.

Практический пример: подсчет частоты (Count Map)

При подсчёте частоты элементов в массиве использование Map упрощает этот процесс.

Следующий код является примером использования Map для подсчета частоты строк в массиве и их сортировки.

1// Count frequencies with Map
2const arr = ['apple','banana','apple','orange','banana','apple'];
3const freq = new Map();
4
5for (const item of arr) {
6  freq.set(item, (freq.get(item) || 0) + 1);
7}
8
9console.log([...freq.entries()]); // [['apple',3], ['banana',2], ['orange',1]]
  • Использование Map позволяет легко проверять наличие и обновлять элементы. Это можно сделать и с объектами, но Map может быть более интуитивен при работе с произвольными ключами.

Замечания по поводу Map и JSON.stringify (Сериализация)

JSON.stringify не может напрямую сериализовать объект Map. Если вам нужно сохранить Map, сначала необходимо его преобразовать.

Следующий пример показывает, как преобразовать Map в массив, прежде чем сериализовать его в JSON, и как его восстановить.

1// Serialize and deserialize a Map
2const m3 = new Map([['x', 1], ['y', 2]]);
3const json = JSON.stringify([...m3]); // convert to array first
4console.log(json); // '[["x",1],["y",2]]'
5
6const restored = new Map(JSON.parse(json));
7console.log(restored.get('x')); // 1
  • Карты, которые необходимо сохранить или передать, следует перед сериализацией преобразовывать в массивы. При восстановлении используйте JSON.parse для преобразования в массив, затем превратите его обратно в Map.

Введение в WeakMap и как его использовать

WeakMap отличается тем, что его ключи имеют слабые ссылки (подвержены сборке мусора).

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

1// WeakMap for metadata tied to object lifecycle
2const wm = new WeakMap();
3let obj = {};
4wm.set(obj, { meta: 'info' });
5console.log(wm.get(obj)); // { meta: 'info' }
6
7obj = null; // now the object can be GC'd and its entry removed from WeakMap
  • WeakMap нельзя перебирать или проверить его размер, но он полезен для предотвращения утечек памяти.

Резюме

Map — это удобная коллекция, которая, в отличие от объектов, допускает любой тип в качестве ключа и сохраняет порядок вставки. Поняв всё — от базовых операций до продвинутого использования, вы сможете более гибко и интуитивно управлять данными. Используя Object и Map соответствующим образом в зависимости от ситуации, вы сможете значительно повысить ясность и читаемость кода.

Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.

YouTube Video