reduceを使って日付一覧から日毎の情報を作成する
こんにちは!
スペースマーケットでフロントエンドエンジニアをしているwharaguchiです。
これまで個人的に使用機会が少なかったreduceを使う機会があったため、自分の備忘録を兼ねて記事にしました。
やりたかったこと
携わっていた施策で以下のキャプチャのような、日付毎にアコーディオンが開閉するUIを作成する必要がありました。
APIから返ってくるデータは、以下のような各日付の情報が一覧で格納された配列でした。
// 元となるデータ
const baseData = [
{
startedAt: "2024-10-30T10:00:00+09:00",
rooms: {
id: "1",
},
},
{
startedAt: "2024-10-30T11:00:00+09:00",
rooms: {
id: "2",
},
},
{
startedAt: "2024-10-31T10:00:00+09:00",
rooms: {
id: "3",
},
},
{
startedAt: "2024-11-01T10:00:00+09:00",
rooms: {
id: "4",
},
},
{
startedAt: "2024-11-01T11:00:00+09:00",
rooms: {
id: "5",
},
},
];
UIを実装するためには、APIから返ってくるデータ構造のままでは実装が難しいと判断し、日付毎に配列を作ることにしました。
// 今回のゴール
const expectData = [
[
{ startedAt: "2024-10-30T10:00:00+09:00", rooms: { id: "1" } },
{ startedAt: "2024-10-30T11:00:00+09:00", rooms: { id: "2" } },
],
[{ startedAt: "2024-10-31T10:00:00+09:00", rooms: { id: "3" } }],
[
{ startedAt: "2024-11-01T10:00:00+09:00", rooms: { id: "4" } },
{ startedAt: "2024-11-01T11:00:00+09:00", rooms: { id: "5" } },
],
];
できたコードと解説
というわけで早速最終的なコードです。
const weeklyShifts = Object.values(
(baseData || []).reduce((acc, record) => {
const date = record?.startedAt.split("T")[0];
return {
...acc,
[date]: [...(acc?.[date] ?? []), record],
};
}, {})
);
以下各コードの解説です。
1. reduceで配列を回す
reduceは配列に対して、指定した処理を繰り返し行うことができます。
同じ配列操作を行うmapやfilterと比較すると、数値の合計値を計算したり、データ構造作成など、出力の形式を柔軟に変更できることが特徴です。
基本構文は以下のとおりです。
array.reduce((accumulator, currentValue, index, array) => {
// ここに処理を書く
}, initialValue);
-
accumulator
: 前回の処理結果が渡される変数。初回はinitialValue
が返されます。省略した場合は、配列の1番目の要素が初期値となります。 -
currentValue
: 現在処理を行っている要素。 -
index
: 現在処理を行っている要素のindex。(※省略可能) -
array
: 元の配列。(※省略可能)
reduceを使ってbaseDataの配列を回します。
わかりやすくするために、console.logで各要素を出力します。
const weeklyShifts = (baseData || []).reduce((acc, record) => {
console.log("acc", acc);
console.log("record", record);
}, {});
// 出力結果
{} // acc
{ startedAt: '2024-10-30T10:00:00+09:00', rooms: { id: '1' } } // record
初期値に空オブジェクトを指定しているので、accには空オブジェクトが、recordには配列の0番目のオブジェクトが渡ってきます。
2. 日付の情報を作成して、分岐を追加する
const weeklyShifts = (baseData || []).reduce((acc, record) => {
const date = record?.startedAt.split("T")[0];
return {
...acc,
[date]: [...(acc?.[date] ?? []), record],
};
}, {});
今回は、日付毎の配列を作りたいため、まずはrecord.startedAt
から日付のデータを作成し、それをオブジェクトのkeyとし、valueにはbaseDataの0番目のデータが格納されます。
// 1周目の結果
{
'2024-10-30': [ { startedAt: '2024-10-30T10:00:00+09:00', rooms: { id: '1' } } ]
}
これを繰り返し、同じkeyがあればそのkeyのvalueに情報をスプレッドしていきます。
// 2周目の結果
{
'2024-10-30': [
{ startedAt: '2024-10-30T10:00:00+09:00', rooms: { id: '1' } },
{ startedAt: '2024-10-30T11:00:00+09:00', rooms: { id: '2' } }
]
}
baseDataの2番目の情報は、日付が異なるため新たなkeyとvalueが生成されます。
{
'2024-10-30': [
{ startedAt: '2024-10-30T10:00:00+09:00', rooms: { id: '1' } },
{ startedAt: '2024-10-30T11:00:00+09:00', rooms: { id: '2' } }
],
'2024-10-31': [ { startedAt: '2024-10-31T10:00:00+09:00', rooms: { id: '3' } } ],
'2024-11-01': [
{ startedAt: '2024-11-01T10:00:00+09:00', rooms: { id: '4' } },
{ startedAt: '2024-11-01T11:00:00+09:00', rooms: { id: '5' } }
]
}
3. Object.values
最後に今回は日毎の配列を作りたいので、Object.values
を使用して日毎の配列を作成します。
const weeklyShifts = Object.values(
(shiftData || []).reduce((acc, record) => {
const date = record?.startedAt.split("T")[0];
return {
...acc,
[date]: [...(acc?.[date] ?? []), record],
};
}, {})
);
[
[
{ startedAt: "2024-10-30T10:00:00+09:00", rooms: { id: "1" } },
{ startedAt: "2024-10-30T11:00:00+09:00", rooms: { id: "2" } },
],
[{ startedAt: "2024-10-31T10:00:00+09:00", rooms: { id: "3" } }],
[
{ startedAt: "2024-11-01T10:00:00+09:00", rooms: { id: "4" } },
{ startedAt: "2024-11-01T11:00:00+09:00", rooms: { id: "5" } },
],
];
完成です!
こちらで期待どおりのデータを作成することができました🥳
さいごに
いかがだったでしょうか?
自分は今までreduceを使う機会が少なかったので、よい機会を得られることができました!🥳
この記事が誰かの助けになれば幸いです。
最後に宣伝です!
スペースマーケットでは現在エンジニアを募集しております!
ちょっと話を聞いてみたいといったようなカジュアルな面談でも構いませんので、ご興味のある方は是非ご応募お待ちしております!
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion
環境が許せば、
Object.groupBy
が使えそうですね。コメントいただきありがとうございます!
またレスが遅くなりまして失礼しました。
Object.groupBy
初めて知ったのですが、確かにObject.groupBy
でもできそうですね...!👀試してみたいと思います!