Я расскажу о том, что по сути является той же проблемой, что и в вопросе, но структурирована по-другому. Не должно быть сложным изменить приведенный ниже код в соответствии с требованиями ОП.
Данные
Предположим, нам даны следующие данные.
require 'time'
SECS_PER_30_MINS = 1800
TIME_FMT = '%H:%M'
Временной интервал начинается с '9:00'
и заканчивается '16:59:59'
, охватывая 16
30-минутные интервалы. Слот 0
от '9:00:00'
до '9:29:59'
, слот 1
от '9:30:00'
до '9:59:59'
и т. Д.
start_time = Time.strptime('9:00', TIME_FMT)
#=> 2020-02-17 09:00:00 -0800
slots = 16.times.map { |i| (start_time + i*SECS_PER_30_MINS)...
(start_time + (i+1)*SECS_PER_30_MINS) }
#=> [2020-02-17 09:00:00 -0800...2020-02-17 09:30:00 -0800,
# 2020-02-17 09:30:00 -0800...2020-02-17 10:00:00 -0800,
# 2020-02-17 10:00:00 -0800...2020-02-17 10:30:00 -0800,
# ...
# 2020-02-17 16:30:00 -0800...2020-02-17 17:00:00 -0800]
Обратите внимание, что это трехточечные диапазоны, поэтому правая конечная точка каждого диапазона не включена в диапазон.
Предположим, у нас есть пять событий со временем начала и окончания следующим образом:
event_times = [['08:00', '09:45'], ['08:30', '10:15'], ['12:34', '14:15'],
['11:30', '14:31'], ['15:30', '16:15']]
event_ranges = event_times.map do |s,e|
Time.strptime(s, TIME_FMT)..Time.strptime(e, TIME_FMT)
end
#=> [2020-02-17 08:00:00 -0800..2020-02-17 09:45:00 -0800,
# 2020-02-17 08:30:00 -0800..2020-02-17 10:15:00 -0800,
# 2020-02-17 12:34:00 -0800..2020-02-17 14:15:00 -0800,
# 2020-02-17 11:30:00 -0800..2020-02-17 14:31:00 -0800,
# 2020-02-17 15:30:00 -0800..2020-02-17 16:15:00 -0800]
Код
def slot_availability(slots, event_ranges)
event_ranges.each_with_object([true]*slots.size) do |er,slot_avail|
slots.each_with_index do |s,i|
slot_avail[i] = false if s.cover?(er.begin) || s.cover?(er.end) ||
er.cover?(s)
end
end
end
Пример
Для массивов slots
и event_ranges
, определенных ранее, получаем:
slot_availability(slots, event_ranges)
# 9:00- 9:30- 10:00- 10:30- 11:00- 11:30- 12:00- 12:30-
#=> [false, false, false, true, true, false, false, false,
# false, false, false, false, true, false, false, true]
# 13:00- 13:30- 14:00- 14:30- 15:00- 15:30- 16:00- 16:30-
Пояснение
Я определил 16-элементный массив slot_avail
, где slot_avail[i]
равно true
(иначе false
), если слот i
не покрыт каким-либо событием. Все эти значения инициализируются как true
.
Затем метод перечисляет события для каждого вопроса о том, попадает ли какая-либо часть события в каждый слот i
. Существует три модели, в которых событие может вмешиваться в слот:
eeeeeeee
ssssssss
eeeeeeee
ssssssss
eeeeeeeeeeee
ssssssss
См. Range # cover? .
Альтернатива использованию Time
objects.
Использование Time
объектов на самом деле стоит здесь очень мало. Ниже показано, как slots
и event_ranges
могут быть определены без использования Time
объектов. Каждый из этих массивов содержит диапазоны, конечными точками которых являются номера минут с предыдущей полуночи. Метод slot_availability
используется без изменений.
Мы начнем с создания простого вспомогательного метода.
def mins_since_midnight(str)
str.split(':').map(&:to_i).zip([60, 1]).map { |a,x| a*x }.sum
end
mins_since_midnight('09:30')
#=> 570
event_times = [['08:00', '09:45'], ['08:30', '10:15'], ['12:34', '14:15'],
['11:30', '14:31'], ['15:30', '16:15']]
event_ranges = event_times.map do |b,e|
mins_since_midnight(b)..mins_since_midnight(e)
end
#=> [480..585, 510..615, 754..855, 690..871, 930..975]
start_time = mins_since_midnight('9:00')
#=> 540
slots = 16.times.map do |i|
start_time + 30*i...start_time + 30*(i+1)
end
#=> [540...570, 570...600, 600...630,..., 990...1020]
slot_availability(slots, event_ranges)
#=> [false, false, false, true, true, false, false, false,
# false, false, false, false, true, false, false, true]