【備忘録】Promise.allの引数をflatMapで作成する

## 背景
"awaite Promise.ALL"と"flatMap"メソッドを用いて、引数に設定した全てのPromseオブジェクトにおける非同期処理のresolveを待つ処理を書くことがあったので備忘録としてまとめます。

## Promise.ALLとは
Promise.ALLは、引数に設定してるPromiseオブジェクトを返す非同期関数の全ての結果がresolveになった際、結果を返すというものです。

### 基本構文
```javascript:index.js
Promise.all([promise1, promise2, promise3])
  .then((results) => {
    console.log(results);
  })
  .catch((error) => {
    console.error(error);
  });
```

今回は上記のpromise1, promise2, promise3をflatMapで作成していきます。

## flatMapとは
flatMapとは、mapのような形で、配列をループして新しい配列を組み直すものではあるのですが、「ネストされた値」を一次元（フラット）にして組み直すものとなります

### mapとの違い
```javascript:index.js
      // map
      const arr = [[1, 2], [3, 4], [5]];

      const newArr = arr.map((item) => {
        return (item = item.map((i) => {
          return i * 2;
        }));
      });

      console.log(newArr);

      // 出力結果
      // [ [ 2, 4 ], [ 6, 8 ], [ 10 ] ]

      // flatMap
      const testArr = [[1, 2], [3, 4], [5]];

      const newTestArr = testArr.flatMap((item) => {
        return (item = item.flatMap((i) => {
          return i * 2;
        }));
      });

      console.log(newTestArr);

      // 出力結果
      // [ 2, 4, 6, 8, 10 ]
```
このように処理後の結果がフラットになります。

## 想定
下記のように、"連続的な接頭辞_ID_何かしらの情報"をプロパティとし各種値がある形としたものがリクエストデータに格納されているとします。
```javascript:index.js
const obj = {
  itemsA_1_name: "Aのアイテム1",
  itemsA_1_number: 10,
  itemsA_2_name: "Aのアイテム2",
  itemsA_2_number: 20,
  itemsB_1_name: "Bのアイテム1",
  itemsB_1_number: 30,
  itemsB_2_name: "Bのアイテム2",
  itemsB_2_number: 40,
  itemsC_1_name: "Cのアイテム1",
  itemsC_1_number: 50,
  itemsC_2_name: "Cのアイテム2",
  itemsC_2_number: 60,
};
```
上記からIDのみを抽出し、かつカテゴリー別の配列（今回で言うとA〜C）を作成し、それを元に取得した値を各種任意のテーブルにインサートしていくといった形なのですが、今回は簡易的にコンソールに出力する形とします。

## 実装
上記のオブジェクトからキーを取り出し、そのキーを引数として値の配列を取得し、その配列に対してmapでPromiseオブジェクトを返す関数を作成します
```javascript:index.js
const obj = {
  itemsA_1_name: "Aのアイテム1",
  itemsA_1_number: 10,
  itemsA_2_name: "Aのアイテム2",
  itemsA_2_number: 20,
  itemsB_1_name: "Bのアイテム1",
  itemsB_1_number: 30,
  itemsB_2_name: "Bのアイテム2",
  itemsB_2_number: 40,
  itemsC_1_name: "Cのアイテム1",
  itemsC_1_number: 50,
  itemsC_2_name: "Cのアイテム2",
  itemsC_2_number: 60,
};

// プレフィックスを元に値を取得する為のカテゴリリスト
const categoryList = ["A", "B", "C"];

const testFn = async () => {
  // 他の処理が実行される
  console.log("start");

  // 全ての非同期処理が終わるまで待つ
  await Promise.all(
    // カテゴリリストを元にflatMapを使用して一次元配列に変換
    categoryList.flatMap((category) => {
      // オブジェクトのキーを元にアイテムIDを取得
      const itemIds = Object.keys(obj)
        .filter((key) => key.startsWith(`items${category}_`) && key.endsWith(`_name`))
        .map((key) => key.split("_")[1]);
      
      // アイテムIDを元にオブジェクトのプロパティ名を取得して、それを元にブラケット方式でオブジェクトにアクセスし、アイテムの名前と数を出力
      return itemIds.map(async (id) => {
        try {
          console.log(`アイテムの名前は: ${obj[`items${category}_${id}_name`]}`);
          console.log(`アイテムの数は: ${obj[`items${category}_${id}_number`]}`);
        } catch (error) {
          console.error(error);
        }
      });
    })
  );

  // 上記が完了次第、他の処理が実行
  console.log("end");
};

testFn();

// コンソールの結果
// start
// アイテムの名前は: Aのアイテム1
// アイテムの数は: 10
// アイテムの名前は: Aのアイテム2
// アイテムの数は: 20
// アイテムの名前は: Bのアイテム1
// アイテムの数は: 30
// アイテムの名前は: Bのアイテム2
// アイテムの数は: 40
// アイテムの名前は: Cのアイテム1
// アイテムの数は: 50
// アイテムの名前は: Cのアイテム2
// アイテムの数は: 60
// end
```
これにて、各種オブジェクトの値を取得することができました。
リクエストボディから特定のプロパティを取得してインサート時のプリペアドステートメントとして活用できます。

### まとめ
まとまった接頭辞とIDが付与されたオブジェクトがリクエストに入っている場合、上記の方法で取り出せます。
また、接頭辞をまとめた配列を作成し、それを元に非同期処理を行う場合はPromise.ALLとflatMapの相性が良いと感じましたので、ひとつの手札として覚えておきたいと思いました。