🍡

連続した予約を1つの予約として判定する

2024/03/25に公開

みなさんこんにちは!
スペースマーケットエンジニアのreyです。

気付けばもう3月も終わりで、来月からは新学期の始まりですね!
時の流れは早いですね〜!

そして、桜が咲くのも早くなっていますね...!! 近所の桜は先月から咲いていました🌸

今回は、普段の実装だとそこまで頻度は多くないのですが、
プログラミングの問題で出てくるような実装に久しぶりに遭遇して、
ちょっとだけ頭を使ったので、そのメモを残しておこうと思います〜

実装の背景

弊社は、レンタルスペースのプラットフォームを運営しております。
https://www.spacemarket.com/

(ぜひこちらのサイトからアクセスできるので覗いてみてください。
今だと、お花見スペースがおすすめです!🌸🌸🌸)

他社様と予約システムを連携した際に、「時間が複数連続した予約を1つの同じ予約として判定して欲しい」とのご要望があったため、そちらを実装しました。

連続した予約を1つの予約として判定する

例えば、ユーザーRさんが、あるスペースに下記のA~Fの6つの予約をしていたとします。

種類 予約時間
予約A 10:00 - 11:00
予約B 11:00 - 12:00
予約C 15:00 - 16:00
予約D 16:00 - 17:00
予約E 17:00 - 18:00
予約F 20:00 - 21:00

今回は、この中から同じユーザーの連続した予約を見つけて、最初と最後の時間を返す、
という内容になります。

例えば、
「予約A」の連続する予約は、「予約A・B」なので、最初の予約がA・最後の予約がBになり、
「予約D」の連続する予約は、「予約C・D・E」なので、最初の予約がC・最後の予約がEになります。

探索する方法は色々あると思いますが、今回は素直に1つずつ見て判定する方法にしました!

実装した内容

「予約D」で実装内容を考えてみます。

予約Dの開始時間と終了時間でそれぞれ前後の予約があるかをDB探索して、
対象の予約があれば、また時間をずらしていき、探索する...
を対象の予約がなくなるまでループする方法を考えました。

1巡目

  • 探索①:予約Dの開始時間「16」を終了時間に持っている予約をDBにアクセスして探す
  • 探索②:予約Dの終了時間「17」を開始時間に持っている予約をDBにアクセスして探す
  • ①が存在する → 予約E
    • 次の探索①の終了時間を「18」にする
    • flagをtrueにする
    • 現時点での最終予約に設定する
  • ②が存在する → 予約C
    • 次の探索②の開始時間を「15」にする
    • flagをtrueにする
    • 現時点での最初の予約に設定する

flagがtrueであれば先頭に戻り、ループ処理をします。

2巡目

  • 探索①:予約Cの開始時間「15」を終了時間に持っている予約をDBにアクセスして探す
  • 探索②:予約Eの終了時間「18」を開始時間に持っている予約をDBにアクセスして探す
  • ①が存在する → 存在しない
    • 探索①の終了時間を「18」にする
    • flagをtrueにする
    • 現時点での最終予約に設定する
  • ②が存在する → 存在しない
    • 探索②の開始時間を「15」にする
    • flagをtrueにする
    • 現時点での最初の予約に設定する

2巡目では、探索時に対象の予約が見つからなかったので、存在チェックがスキップされて、
ループ処理を抜けます。

Railsのコードに落とし込むと、下記のようなコードになると思います。

初期値を設定します。

res_flag = true
res_start = reservation.started_at
res_end = reservation.ended_at

ループ処理を実装します。

while res_flag do
    res_flag = false
    next_res = Reservation.find_by(room_id: reservation.room_id, user_id: reservation.user_id, started_at: res_end)
    prev_res = Reservation.find_by(room_id: reservation.room_id, user_id: reservation.user_id, ended_at: res_start)
    if next_res.present?
        res_end = next_res.ended_at
        last_res = next_res
        res_flag = true
    end
    if prev_res.present?
        res_start = prev_res.started_at
        first_res = prev_res
        res_flag = true
    end
end

たまにアルゴリズムのような問題が出てくると、頭の体操になります。
今回は、丁寧に1つずつ確認する方法を取りましたが、もっと素晴らしい方法がもしあれば、
ぜひ教えてください〜!

最後に

スペースマーケットでは一緒に働く仲間を募集しています!
新しい技術に興味がある方も、手を上げればチャレンジできる環境だと思います。
カジュアルに話を聞きたいだけという方でも大歓迎ですので、ちょっとでも興味があれば
こちらからご応募をお待ちしております!

スペースマーケット Engineer Blog

Discussion