Сравните два списка, чтобы найти совпадения даты и времени в эликсире - PullRequest
0 голосов
/ 12 июня 2019

Как часть некоторого кода, который запускает систему бронирования, у нас есть список time_slots, которые являются кортежами, содержащими {start_time, end_time}.Это доступные временные интервалы, которые можно забронировать:

time_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]}
]

Затем у нас также есть список заказов, который содержит списки кортежей, содержащих каждый {booking_start, booking_end}.

bookings = [
  [
    {~N[2019-06-13 09: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]}
  ]
]

Inв этом случае мы хотели бы, чтобы результаты были двумя заказами со всеми их time_slots заполненными:

  • 2019-06-13
  • 2019-06-20

Поскольку они заполняют все свои временные интервалы, а затем возвращают эти результаты как Date с.


Чтобы предоставить немного больше информации:

  • Чтобы заполнить временной интервал, потребуется, чтобы начало или окончание бронирования перекрывались внутри него (независимо от того, насколько малое это перекрытие):
    • Например, бронирование 0900–1000 заполнит 0900–1300, 0900–1700 и 0900–2100 временных интервалов
  • Временной интервал может быть заполнен более чем одним бронированием:
    • Например, мы можемиметь заказы 0900–1000 и 1000–1200, которые вписываются в интервал времени 0900–1300.
  • При наличии бронирования tесли он простирается за пределы наибольшего временного интервала, он считается заполненным:
    • Например, резервирование 0800—2200 заполнило бы 0900–2100 временной интервал (вместе со всеми остальными)

Ответы [ 3 ]

2 голосов
/ 13 июня 2019

Итак, мое понимание вопроса таково: для списка бронирований все ли временные интервалы конфликтуют хотя бы с одним бронированием?

На конфликтующее бронирование можно ответить, проверив две вещи:

  1. Если бронирование начинается ДО начала временного интервала, оно конфликтует, если бронирование завершается ПОСЛЕ запуска временного интервала.

  2. Если бронирование начинается ВКЛ ИЛИ ПОСЛЕ временислот начинается, он конфликтует, если БРОНИРОВАНИЕ начинается до окончания временного интервала.

Поэтому рабочий код будет выглядеть следующим образом:

time_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]}
]

bookings = [
  [
    {~N[2019-06-13 09: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-13 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]}
  ]
]

bookings
|> Enum.filter(fn booking ->
  Enum.all?(time_slots, fn {time_start, time_end} ->
    Enum.any?(booking, 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)
end) 
|> Enum.map(fn [{booking_start, _} | _] -> NaiveDateTime.to_date(booking_start) end)

PS: обратите внимание, что вы не должнысравните время / дату / дату с >, < и друзьями.Всегда используйте соответствующие функции сравнения.

1 голос
/ 13 июня 2019

Если у вас есть опечатка во втором бронировании, и она не начинается почти неделю после само по себе, решение может быть намного проще, чем тщательное сокращение.

Слоты заполняются, когда бронирование начинается и заканчивается точно в:

{start, end} =
  time_slots
  |> Enum.flat_map(&Tuple.to_list/1)
  |> Enum.min_max()          
#⇒ {~T[09:00:00], ~T[21:00:00]}

Что делает проверку почти тривиальной:

Enum.filter(bookings, fn booking ->
  {s, e} = {Enum.map(booking, &elem(&1, 0)), Enum.map(booking, &elem(&1, 1))}
  with {[s], [e]} <- {s -- e, e -- s} do
    same_date =
      [s, e]
      |> Enum.map(&NaiveDateTime.to_date/1)
      |> Enum.reduce(&==/2)
    full = Enum.map([s, e], &NaiveDateTime.to_time/1)

    same_date and full == [start, end]
  end
end)

Kernel.SpecialForms.with/1 гарантирует, что все, что не ожидается, будет отфильтровано.

1 голос
/ 13 июня 2019

Хотя это может не охватывать все случаи, учитывая предоставленные вами данные выборки, это будет работать

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, если у вас есть пустой список, это означает, что в течение дня не осталось свободных мест. Таким образом, вы добавляете его во внешний аккумулятор, это будет список полностью забронированных дней.

В конце вы снова уменьшаете результаты, чтобы инвертировать их, и в процессе каждого элемента задаете дату, а не список заказов на день.

...