Mapについて

# 注意

あらかじめ言っておきますが、筆者は初心者なので、自分なりの解釈でまとめています。「100%信じるぜ！」という気持ちで読まないようお願いします。どうぞよろしく！

# 参考資料
[MDN: Map - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map)

# 経緯
ポートフォリオを作成している際、以下のような課題に直面しました。

たとえば、以下のような `ingredients` テーブルがあったとします。  
このテーブルを使って「レシピごとの材料を集計」したい場面です。  
たとえば「レシピ番号 2 と 3 を選んだら、たまねぎが 2 個になる」といった処理をしたいと考えました。
### ingredients テーブルの例

| レシピ番号 | 材料     | 数   | 単位 |
|------------|----------|------|------|
| 1          | じゃがいも | 1    | 本   |
| 1          | 人参     | 1    | 本   |
| 1          | 豚肉     | 300  | g    |
| 2          | たまねぎ | 1    | 個   |
| 2          | 豚肉     | 500  | g    |
| 3          | たまねぎ | 1    | 個   |
| 3          | ひき肉   | 500  | g    |
| 3          | 卵       | 2    | 個   |
| 3          | 片栗粉   | 少々 |      |

このように、材料名と単位が整っていれば、単純に `for` 文などで集計処理できそうです。

ただし、以下のように**同じ材料で単位が異なる場合（例：たまねぎが "300g" と "1個"）**、どのように集計するか悩みました。

### 単位がバラバラなパターン（例）

| レシピ番号 | 材料     | 数   | 単位 |
|------------|----------|------|------|
| 1          | じゃがいも | 1    | 個   |
| 1          | 人参     | 1    | 本   |
| 1          | 豚肉     | 300  | 個   |
| 2          | たまねぎ | 300  | g    |
| 2          | 豚肉     | 500  | g    |
| 3          | たまねぎ | 1    | 個   |
| 3          | ひき肉   | 500  | g    |
| 3          | 卵       | 2    | 個   |
| 3          | 片栗粉   | 少々 |      |

そこで、ChatGPT に聞いたところ「**Map 関数を使うといいよ！**」というヒントをもらい、調べてまとめてみることにしました。

# Mapとは？ <a id="mapとは"></a>

## Map とは

`Map` は、キーと値を柔軟に設定できるコレクションです。  
通常のオブジェクト（`{}`）ではキーが**文字列か記号に限定**されますが、Map は**任意の値をキー**に使えます。

## キーの違い

オブジェクトの場合：

```javascript
const obj = {
  name: "taro",
  age: 28
};
```
Mapの場合

```
const map = new Map();
map.set(1, "taro");
map.set("age", 28);
```
上記のように、1 のような数値をキーにできるのが特徴です。

# Mapの操作<a id="mapの操作"></a>

```javascript
// 新しく Map を作成
const map = new Map();

// 値を追加
map.set(key, value);

// キーの存在確認
map.has(key);

// 値を取得
map.get(key);
```
などのメソッドが用意されています。
[等々で紹介しましたが、詳細は MDN を参照ください](#参考資料)

## その他
Map はオブジェクトと同様に `forEach` を使った反復処理が可能です。
>MDNより引用
>
>```javascript
>myMap.forEach((value, key) => {
 > console.log(`${key} = ${value}`);
>});
>// 0 = zero
>// 1 = one
>```


# 「Map便利。じゃあオブジェクトいらない？」。<a id="比較"></a><a id="比較"></a>
一見すると Map は非常に便利に見えますが、**オブジェクトにも使いやすさがあります。**

たとえば、Map で初期データをセットするには：
```javascript
map1.set("a", 1);
map1.set("b", 2);
map1.set("c", 3);
```
一方、オブジェクトなら簡潔に書けます：
```javascript
const test = {
    'a':1,
    'b':2,
    'c':3,
}
```

そのため、**初期設定が多い場合や JSON として扱いたい場合はオブジェクトの方が適しています。**

# 今回の使用方法<a id="今回の使用方法"></a>

## まずは、Mapの作成
```
const sumIngredints = new Map<string, IngredientsType>
```

## `ingredients` 配列を `forEach` で回して集計する
```typescript
ingredients.forEach(i => {
  //以下のようにMapに設定するキーを設定する。今回は、材料の名前と単位をキーとした。
  const key = `${i.ingredientName}_${i.IngredientUnit}`
  //Mapにキーが有るかどうかを判定して、なければ、Mapにセットする。
  if (!sumIngredints.has(key)) {
    sumIngredints.set(key, {
      ingredientName: i.ingredientName,
      ingredientQuantity: i.ingredientQuantity,
      ingredientUnit: i.IngredientUnit
    })
  //キーがあれば、それに数量を足すことになる
  } else {
    sumIngredints.get(key)!.ingredientQuantity += i.ingredientQuantity
  }
```
このようにして、**材料名＋単位**をキーとして、材料の合計数量を算出できます。
# 感想

Map関数は非常に協力だと感じました。ただ、、使いやすいパターンと使いにくいパターンがあると思うので、今後も使い分けを意識していきたいです。ガンガンアプリ開発していこう。