# 01. Map

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

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

* В объектах в которых ключами могут быть только строки, в `Map` ключом может быть произвольное значение, в том числе объект или функция.
* Объект имеет прототипы, что влияет на его ключи. Карты с этой точки зрения выступают как "чистые объекты".
* Можно легко получить размер карты, размер объектов много сложнее.
* Карта сохраняет пары ключ-значение в порядке их добавления.
* Перебор гарантированно осуществляется в порядке вставки.
* На картах реализованы символы
  * `Symbol.iterator`
  * `Symbol.toStringTag`
  * `Symbol.species`

## Create Map

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

```javascript
// 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:

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

## `Map` API

[Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)

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

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

* `size` - число записей в `Map`.
* `clear()` - удаляет все записи из карты.
* `delete()` - удаляет запись с ключом `key`, возвращает `true`, если такая запись была, иначе `false`.
* `has()` - возвращает `true`, если ключ есть, иначе `false`.
* `get()` - возвращает значение по ключу или `undefined`
* `set()` - ассоциирует значение с ключом и возвращает текущую карту.

```javascript
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`.

```javascript
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.

```javascript
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` операторы в конструкторе:

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

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

```javascript
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 и обратно:

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

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

```javascript
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;
}
```
