Хотя это может не охватывать все случаи, учитывая предоставленные вами данные выборки, это будет работать
defmodule BookingsTest do
@slots [
{~T[09:00:00], ~T[13:00:00]},
{~T[09:00:00], ~T[17:00:00]},
{~T[09:00:00], ~T[21: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]}
]
def booked_days(bookings, time_slots \\ @slots) do
Enum.reduce(bookings, [], fn(day_bookings, acc) ->
Enum.reduce(day_bookings, time_slots, fn({%{hour: s_time}, %{hour: e_time}}, ts) ->
Enum.reduce(ts, [], fn
({%{hour: slot_s}, %{hour: slot_e}} = slot, inner_acc) ->
case is_in_slot(s_time, e_time, slot_s, slot_e) do
true -> inner_acc
_ -> [slot | inner_acc]
end
end)
end)
|> case do
[] -> [day_bookings | acc]
_ -> acc
end
end)
|> Enum.reduce([], fn([{arb, _} | _], acc) -> [NaiveDateTime.to_date(arb) | acc] end)
end
def is_in_slot(same_start, _, same_start, _), do: true
def is_in_slot(s_time, e_time, slot_s, slot_e) when s_time < slot_s and e_time > slot_s, do: true
def is_in_slot(s_time, e_time, slot_s, slot_e) when s_time > slot_s and s_time < slot_e, do: true
def is_in_slot(_, _, _, _), do: false
end
> bookings = [
[
{~N[2019-06-13 10:00:00], ~N[2019-06-13 17:00:00]},
{~N[2019-06-13 17:00:00], ~N[2019-06-13 21:00:00]}
],
[{~N[2019-06-20 09:00:00], ~N[2019-06-20 21:00:00]}],
[
{~N[2019-06-22 13:00:00], ~N[2019-06-22 17:00:00]},
{~N[2019-06-22 17:00:00], ~N[2019-06-22 21:00:00]}
]
]
> BookingsTest.booked_days(bookings)
[~D[2019-06-13], ~D[2019-06-20]]
Идея состоит в том, чтобы сократить список bookings
, накапливаясь в пустой список, каждое перечисление будет списком занятых слотов за день.
Сократить этот список, накапливая список всех доступных временных интервалов.
Внутри этого уменьшите через временной интервал аккумулятор в пустой список.
Для каждого слота проверьте, совпадает ли время начала и окончания слота бронирования текущего дня с этим слотом. Если это так, просто верните внутренний аккумулятор как есть. Если этого не произойдет, добавьте слот в этот аккумулятор.
В конце сокращения day_bookings
, если у вас есть пустой список, это означает, что в течение дня не осталось свободных мест. Таким образом, вы добавляете его во внешний аккумулятор, это будет список полностью забронированных дней.
В конце вы снова уменьшаете результаты, чтобы инвертировать их, и в процессе каждого элемента задаете дату, а не список заказов на день.