[JavaScript] Mapと連想配列の相互変換

Mapオブジェクトと連想配列を変換したくなったのですが、一発で変換できないみたいなので自分で書いてみました。
調べながら書いていろいろ勉強になったのでメモしておきます。

# できたもの
```js
const mapToObject = map =>
  Array.from(map.entries())
    .map(([k,v]) => ({[k]:v}))
    .reduce((l,r) => Object.assign(l, r), {})

const objectToMap = object =>
  Object.entries(object).reduce((l,[k,v])=>l.set(k,v), new Map())

```

## 追記
編集リクエストやコメントをもらって最終的にできたもの

```js
const mapToObject = map =>
  [...map].reduce((l,[k,v]) => Object.assign(l, {[k]:v}), {})

const objectToMap = object =>
  new Map(Object.entries(object))
```


# 説明

## Mapを連想配列に変換

```js
const map = new Map()
map.set('id', 1)
map.set('name', 'me')
// map -> Map {id: 1, name: 'me'}

let tmp = map.entries(map)
// tmp -> MapIterator{ ['id', 1], ['name', 'me'] }

tmp = Array.from(tmp)
// tmp -> [ ['id', 1], ['name', 'me'] ]

tmp = tmp.map(([k,v]) => ({[k]:v}))
// tmp -> [ {id: 1}, {name: 'me'} ]

const result = tmp.reduce((l,r) => Object.assign(l, r), {})
// result -> {id: 1, name:'me'}
```

### Mapからイテレータを取り出す
はじめに[Map#entries](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map/entries)メソッドで、mapから「キーと値のペア」のイテレータを取り出します。


```js
let tmp = map.entries(map)
// tmp -> MapIterator{ ['id', 1], ['name', 'me'] }
```

### イテレータを配列に変換
イテレータというのは配列ではないので、`map`も`reduce`も使えません。
不便なので配列に変換します。[Array.from](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/from)メソッドにイテレータを渡すと配列に変換してくれます。これでやっと配列オブジェクトの便利なメソッドが使えるようになりました。


```js
tmp = Array.from(a)
// tmp -> [ ['id', 1], ['name', 'me'] ]
```

### 配列を連想配列に変換
配列の要素である各「キーと値のペア」は、それぞれが長さ2の配列になっていて、0番目にキー、1番目に値が入っています。これを連想配列にします。

#### 分割代入
ES6の[分割代入](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)という機能で、配列引数を最初から展開された状態で受け取れるようになりました。


```js
const plus = nums => nums[0] + nums[1]
// ↓
const plus = ([a, b]) => a + b
```

#### Computed property names
さらに、連想配列のキー名に変数を使った書き方ができるようになりました。
[オブジェクト初期化子 - Computed property names](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Object_initializer#%E6%A7%8B%E6%96%87)

```js
const key1 = 'id'
const key2 = 'name'
const obj = {[key1]: 1, [key2]: 'me'} // -> {id: 1, name: 'me'}
```

これらを使用して、長さ2の配列を連想配列に変換します。外側の配列の要素全てに対して[Array#map](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)で行います。


```js
tmp.map(([k,v]) => ({[k]:v}))
// tmp -> [ {id: 1}, {name: 'me'} ]
```

### 結合して完成
連想配列の配列になったので、全ての連想配列を[Object.assign](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)で結合したらできあがりです。
配列の要素を全部結合するのに[Array#reduce](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)を使っています。


```js
tmp.reduce((l,r) => Object.assign(l, r), {})
// tmp -> {id: 1, name:'me'}
```

## 連想配列をMapに変換
こっちは簡単でした。

```js
const object = {id: 1, name: 'me' }
// object ->  {id: 1, name: 'me' }

let tmp =  Object.entries(object)
// tmp -> [ ['id', 1], ['name', 'me'] ]

const result = new Map(tmp)
// result -> Map {id: 1, name: 'me' }
```

[Object.entries](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/entries)はES2017の機能ですが、IE11以外のブラウザならだいたい動きます。nodejsでは8.7以降くらいから動くようです。

# 編集リクエストをいただきました！
_※ 1月18日更新_

Mapから連想配列に変換する処理について、編集リクエストをもらいました！
@hoo-chan さんありがとうございますm(_ _)m
プルリクをもらったみたいでうれしいですね(´∀｀*)

```js
const mapToObject = map =>
  [...map].reduce((l,[k,v]) => Object.assign(l, {[k]:v}), {})
```

## Mapオブジェクトは最初からイテレータブル
Mapオブジェクトは、それ自体がイテレータブルなので、`Array.from`にそのまま渡すことができます。
さらに、イテレータブルってことはスプレッド演算子で配列に展開できそうです。

```js
Array.from(map.entries())
// map -> [ [ 'id', 1 ], [ 'name', 'me' ] ]
```
👇

```js
Array.from(map)
// map -> [ [ 'id', 1 ], [ 'name', 'me' ] ]
```

👇

```js
[...map]
// map -> [ [ 'id', 1 ], [ 'name', 'me' ] ]
```

## map().reduce()は冗長
`Array#map`で変換してから`Array#reduce`に渡してましたが、reduceの中で変換もすれば一つにまとめられます。

```js
tmp.map(([k,v]) => ({[k]:v}))
  .reduce((l,r) => Object.assign(l, r), {})
```
👇

```js
tmp.reduce((l,[k:v]) => Object.assign(l, {[k]:v}), {})
```

## 完成
最終的にこうなりました。
スッキリしましたね。

```js
const mapToObject = map =>
  Array.from(map.entries())
    .map(([k,v]) => ({[k]:v}))
    .reduce((l,r) => Object.assign(l, r), {})
```
↓

```js
const mapToObject = map =>
  [...map].reduce((l,[k,v]) => Object.assign(l, {[k]:v}), {})
```

## ループで回した方がわかりやすい
Mapオブジェクトがイテレータブルってことは`for ... of`のループで回すことができます。関数型っぽい書き方にこだわらなければこっちの方がわかりやすくてよいですね。

```js
const mapToArray = map => {
  const result = {}
  for (const [key, value] of map) result[key] = value
  return result
}
```