gosseti
gosseti

Reputation: 975

Filter overlapping time periods using Elixir

I have two lists of tuples that contain start and end times. All the start and end times are Time structs.

I want to filter out the time_slots that overlap with any booked_slots:

time_slots = [
  {~T[09:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[21:00:00]},
  {~T[17:00:00], ~T[21:00:00]},
  {~T[09:00:00], ~T[13:00:00]},
  {~T[09:00:00], ~T[21:00:00]}
]

booked_slots = [{~T[14:00:00], ~T[21:00:00]}]

Which should leave us with:

[
  {~T[09:00:00], ~T[13:00:00]}
]

I have tried the following method (as adapted from the answer to a previous related question: Compare two lists to find date and time overlaps in Elixir):

Enum.filter(time_slots, fn {time_start, time_end} ->
  Enum.any?(booked_slots, fn {booking_start, booking_end} ->
    if Time.compare(booking_start, time_start) == :lt do
      Time.compare(booking_end, time_start) == :gt
    else
      Time.compare(booking_start, time_end) == :lt
    end
  end)
end)

However this returns:

[
  {~T[09:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[21:00:00]},
  {~T[17:00:00], ~T[21:00:00]},
  {~T[09:00:00], ~T[21:00:00]}
]

We also need to factor in times that may be equal, but do not overlap. For example:

time_slots = [
  {~T[09:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[21:00:00]},
  {~T[17:00:00], ~T[21:00:00]},
  {~T[09:00:00], ~T[21:00:00]},
  {~T[09:00:00], ~T[13:00:00]}
]

booked_slots = [{~T[17:00:00], ~T[21:00:00]}]

Should return…

[
  {~T[09:00:00], ~T[17:00:00]},
  {~T[13:00:00], ~T[17:00:00]},
  {~T[09:00:00], ~T[13:00:00]}
]

Upvotes: 1

Views: 233

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

You need to filter out all the time slots having either start or end times inside the booked slot.

Enum.reject(time_slots, fn {ts, te} ->
  Enum.any?(booked_slots, fn {bs, be} ->
    (Time.compare(ts, bs) != :lt and Time.compare(ts, be) == :lt) or
    (Time.compare(te, bs) == :gt and Time.compare(te, be) != :gt) or
    (Time.compare(ts, bs) == :lt and Time.compare(te, be) == :gt)
  end)
end)

The first condition checks for slots having start time inside the booked slot, the second one checks for those having end time within the booked slots, and the third one checks for those containing the whole booked slot.

Upvotes: 3

Related Questions