01. Map

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

Отличия карт от объектов:

  • В объектах в которых ключами могут быть только строки, в Map ключом может быть произвольное значение, в том числе объект или функция.

  • Объект имеет прототипы, что влияет на его ключи. Карты с этой точки зрения выступают как "чистые объекты".

  • Можно легко получить размер карты, размер объектов много сложнее.

  • Карта сохраняет пары ключ-значение в порядке их добавления.

  • Перебор гарантированно осуществляется в порядке вставки.

  • На картах реализованы символы

    • Symbol.iterator

    • Symbol.toStringTag

    • Symbol.species

Create Map

При создании Map можно сразу инициализировать списком значений. Аргументом new Map должен быть итерируемый объект (не обязательно именно массив):

// Create new empty map.
const map = new Map();

// From array of pairs or from any iterable of pairs
const map = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"] // trailing comma is ignored
]);

const map2 = new Map()
  .set(1, "one")
  .set(2, "two")
  .set(3, "three");

Object.entries() also lets you set up a Map via an object. This is more concise than using an Array of 2-element Arrays, but keys can only be strings:

let map = new Map(
  Object.entries({
    one: 1,
    two: 2
  })
);

Map API

Map

Для сохранения и чтения значений используются методы get и set. И ключи и значения сохраняются «как есть», без преобразований типов.

Другие методы:

  • size - число записей в Map.

  • clear() - удаляет все записи из карты.

  • delete() - удаляет запись с ключом key, возвращает true, если такая запись была, иначе false.

  • has() - возвращает true, если ключ есть, иначе false.

  • get() - возвращает значение по ключу или undefined

  • set() - ассоциирует значение с ключом и возвращает текущую карту.

let map = new Map();

map.set(1, "Anton").set("2", "Nastya");

map.size; //  2
map.get(1); // Anton
map.get("2"); // Nastya
map.get(2); // undefined

Для проверки значений на эквивалентность используется алгоритм SameValueZero. Он аналогичен строгому равенству ===, отличие — в том, что NaN считается равным NaN. Поэтому значение NaN также может быть использовано в качестве ключа.

Map iteration

Для итерации по map используется один из трёх методов:

  • map.keys() — возвращает итерируемый объект для ключей,

  • map.values() — возвращает итерируемый объект для значений,

  • map.entries() — возвращает итерируемый объект для записей [ключ, значение], он используется по умолчанию в for..of.

  • for..of - аналогичен вызову map.entries()

  • [...map] - аналогичен вызову метода map.entries().

  • Карты имеют метод forEach, но не имеют методов map и filter.

const map = new Map().set(false, "no").set(true, "yes");

for (const [key, value] of map.entries()) {
  console.log(key, value);
}

Notice that map.values() and map.entries() return iterator objects. To put the result into an array, the spread operator ... is necessary. In a for..of loop statement the iterator can be used directly.

const map = new Map().set(false, 'no').set(true, 'yes');
[...map.keys()]     // [ false, true ]
[...map.values()]   // ['no', 'yes']
[...map.entries()]  // [[false, 'no'], [true, 'yes']]

Карты можно комбинировать используя spread операторы в конструкторе:

const combinedMap = new Map([...map1, ...map2])
[...combinedMap] // convert to Array to display
[ [ 1, 'a1' ],
  [ 2, 'b2' ],
  [ 3, 'c2' ],
  [ 4, 'd2' ] ]

К карты не имеют функции map, но её можно соответствующим образом эмулировать:

let map0 = new Map([
  [1, "a"],
  [2, "b"],
  [3, "c"]
]);

let map1 = new Map( // step 3
  [...map0] // step 1
    .map(([k, v]) => [k * 2, "_" + v]) // step 2
);
// Resulting Map: {2 -> '_a', 4 -> '_b', 6 -> '_c'}

Аналогичным образом можно эмулировать функцию filter.

Conversions

Конверсия карты в JSON и обратно:

function mapToJson(map) {
  return JSON.stringify([...map]);
}
function jsonToMap(jsonStr) {
  return new Map(JSON.parse(jsonStr));
}

Конверсия карт в объекты возможна только если все ключи карты являются строками:

function mapToObj(map) {
  const obj = Object.create(null);
  for (const [k, v] of map) {
    // We don’t escape the key '__proto__'
    // which can cause problems on older engines
    obj[k] = v;
  }
  return obj;
}

function objToMap(obj) {
  const map = new Map();
  for (const k of Object.keys(obj)) {
    map.set(k, obj[k]);
  }
  return map;
}

Last updated