[英]Can't iterate over Time objects in Ruby
我正在写一个约会表格,让用户选择日期。 然后,它将获取日期并对照Google日历检查从10:00 am到5:00 pm的30分钟时间间隔内该日期的可用时隙。
在我的Calendar类中,我有一个available_times
方法:
def available_times(appointment_date)
appointment_date_events = calendar.events.select { |event| Date.parse(event.start_time) == appointment_date }
conflicts = appointment_date_events.map { |event| [Time.parse(event.start_time), Time.parse(event.end_time)] }
results = resolve_time_conflicts(conflicts)
end
此方法需要一个日期和抓住start_time
和end_time
在该日期的每个事件。 然后,它调用resolve_time_conflicts(conflicts)
:
def resolve_time_conflicts(conflicts)
start_time = Time.parse('10:00am')
available_times = []
14.times do |interval_multiple|
appointment_time = (start_time + interval_multiple * (30 * 60))
available_times << appointment_time unless conflicts.each{ |conflict| (conflict[0]..conflict[1]).include?(appointment_time)}
end
available_times
end
当我尝试遍历冲突数组时,抛出“无法遍历时间”错误。 我试图在冲突数组上调用to_enum
,但仍然遇到相同的错误。
我在SO上看到的所有其他问题都在引用step
方法,这似乎不适用于这种情况。
更新:
Thanks @caryswoveland and @fivedigit. I combined both of your answers, which were very helpful for different aspects of my solution:
def available_times(appointment_date)
appointment_date_events = calendar.events.select { |event| Date.parse(event.start_time) == appointment_date }
conflicts = appointment_date_events.map { |event| DateTime.parse(event.start_time)..DateTime.parse(event.end_time) }
results = resolve_time_conflicts(conflicts)
end
def resolve_time_conflicts(conflicts)
date = conflicts.first.first
start_time = DateTime.new(date.year, date.month, date.day, 10, 00).change(offset: date.zone)
available_times = []
14.times do |interval_multiple|
appointment_time = (start_time + ((interval_multiple * 30).minutes))
available_times << appointment_time unless conflicts.any? { |conflict| conflict.cover?(appointment_time)}
end
available_times
end
问题出在此位:
(conflict[0]..conflict[1]).include?(appointment_time)
# TypeError: can't iterate from Time
您正在创建一个时间范围,然后检查appointment_time
时间是否在该范围内。 这就是导致您遇到错误的原因。
而不是include?
,您应该使用cover?
:
(conflict[0]..conflict[1]).cover?(appointment_time)
假设conflict[0]
是最早的时间。
例外
@fivedigit已解释了为什么引发异常。
其他问题
需要any?
each
:
appointment_times = []
#=> []
appointment = 4
#=> 4
conflicts = [(1..3), (5..7)]
#=> [1..3, 5..7]
appointment_times << 5 unless conflicts.each { |r| r.cover?(appointment) }
#=> nil
appointment_times
#=> []
appointment_times << 5 unless conflicts.any? { |r| r.include?(appointment) }
#=> [5]
appointment_times
#=> [5]
我建议您将appointment_time
Time
[start_time, end_time]
到一个Time
对象上,进行conflicts
和元素数组[start_time, end_time]
,然后将appointment_time
时间与端点进行比较:
...unless conflicts.any?{ |start_time, end_time|
start_time <= appointment_time && appointment_time <= end_time }
旁: 范围#包括? 当端点为“数字”时,仅查看端点(与Range#cover? does
)。 Range#include?
当它们是Time
对象时,只需要查看端点即可,但是我不知道Ruby是否将Time
对象视为“数字”。 我想可以看看源代码。 有人知道吗?
替代方法
我想建议一种不同的方法来实现您的方法。 我将举一个例子。
假设约会以15分钟为单位,第一个时间段是10:00 am-10:15am,最后一个时间是4:45 pm-5:00pm。 (块的持续时间当然可以短至1秒。)
让10:00 am-10:15am成为第0块,让10:15 am-10:30am成为第1块,依此类推,直到第27块,即4:45 pm-5:00pm。
接下来,将conflicts
表示为块范围的数组,由[start, end]
。 假设在以下地点有约会:
10:45am-11:30am (blocks 3, 4 and 5)
1:00pm- 1:30pm (blocks 12 and 13)
2:15pm- 3:30pm (blocks 17, 18 and 19)
然后:
conflicts = [[3,5], [12,13], [17,19]]
您必须编写一个返回conflicts
的方法reserved_blocks(appointment_date)
。
其余代码如下:
BLOCKS = 28
MINUTES = ["00", "15", "30", "45"]
BLOCK_TO_TIME = (BLOCKS-1).times.map { |i|
"#{i<12 ? 10+i/4 : (i-8)/4}:#{MINUTES[i%4]}#{i<8 ? 'am' : 'pm'}" }
#=> ["10:00am", "10:15am", "10:30am", "10:45am",
# "11:00am", "11:15am", "11:30am", "11:45am",
# "12:00pm", "12:15pm", "12:30pm", "12:45pm",
# "1:00pm", "1:15pm", "1:30pm", "1:45pm",
# "2:00pm", "2:15pm", "2:30pm", "2:45pm",
# "3:00pm", "3:15pm", "3:30pm", "3:45pm",
# "4:00pm", "4:15pm", "4:30pm", "4:45pm"]
def available_times(appointment_date)
available = [*(0..BLOCKS-1)]-reserved_blocks(appointment_date)
.flat_map { |s,e| (s..e).to_a }
last = -2 # any value will do, can even remove statement
test = false
available.chunk { |b| (test=!test) if b > last+1; last = b; test }
.map { |_,a| [BLOCK_TO_TIME[a.first],
(a.last < BLOCKS-1) ? BLOCK_TO_TIME[a.last+1] : "5:00pm"] }
end
def reserved_blocks(date) # stub for demonstration.
[[3,5], [12,13], [17,19]]
end
让我们看看我们得到了什么:
available_times("anything")
#=> [["10:00am", "10:45am"],
# ["11:30am", "1:00pm"],
# [ "1:45pm", "2:15pm"],
# [ "3:00pm", "5:00pm"]]
说明
这是正在发生的事情:
appointment_date = "anything" # dummy for demonstration
all_blocks = [*(0..BLOCKS-1)]
#=> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
# 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
reserved_ranges = reserved_blocks(appointment_date)
#=> [[3, 5], [12, 13], [17, 19]]
reserved = reserved_ranges.flat_map { |s,e| (s..e).to_a }
#=> [3, 4, 5, 12, 13, 17, 18, 19]
available = ALL_BLOCKS - reserved
#=> [0, 1, 2, 6, 7, 8, 9, 10, 11, 14, 15, 16, 20, 21, 22, 23, 24, 25, 26, 27]
last = -2
test = false
enum1 = available.chunk { |b| (test=!test) if b > last+1; last = b; test }
#=> #<Enumerator: #<Enumerator::Generator:0x00000103063570>:each>
我们可以将其转换为数组,以查看如果不遵循map
它将传递给块的值:
enum1.to_a
#=> [[true, [0, 1, 2]],
# [false, [6, 7, 8, 9, 10, 11]],
# [true, [14, 15, 16]],
# [false, [20, 21, 22, 23, 24, 25, 26, 27]]]
Enumerable#chunk对枚举数的连续值进行分组。 通过对test
的值进行分组并在遇到非连续值时将其值在true
和false
之间翻转来true
。
enum2 = enum1.map
#=> #<Enumerator: #<Enumerator: (cont.)
#<Enumerator::Generator:0x00000103063570>:each>:map>
enum2.to_a
#=> [[true, [0, 1, 2]],
# [false, [6, 7, 8, 9, 10, 11]],
# [true, [14, 15, 16]],
# [false, [20, 21, 22, 23, 24, 25, 26, 27]]]
您可能会将enum2
视为“复合”枚举器。
最后,我们将传递到块中的enum2
的每个值的第二个元素(块变量a
,等于传递的第一个元素的[0,1,2]
)转换为以12小时表示的范围。 enum2
的每个值的第一个元素( true
或false
)都没有使用,因此我用下划线替换了其块变量。 这提供了所需的结果:
enum2.each { |_,a|[BLOCK_TO_TIME[a.first], \
(a.last < BLOCKS-1) ? BLOCK_TO_TIME[a.last+1] : "5:00pm"] }
#=> [["10:00am", "10:45am"],
# ["11:30am", "1:00pm"],
# [ "1:45pm", "2:15pm"],
# [ "3:00pm", "5:00pm"]]
将范围从时间范围转换为整数范围:
range = (conflict[0].to_i..conflict[1].to_i)
然后像使用include?
一样使用===
运算符include?
:
conflict === appointment_time
编辑:您还可以显然将appointment_time
时间转换为整数,仍然使用include?
因为该范围现在只是整数范围。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.