我正在处理一个表单,用户输入一个日期范围并从复选框列表中选择一周中的一天/几天,即周日、周一、周二、周三、周四、周五和周六。
提交表单后,我需要一种方法来获取基于所选日期输入的两个日期之间的日期列表,即两个给定日期之间的所有星期一和星期四。我已经浏览了文档,但无法指出如何有效地做到这一点,即 ruby 方式。
我正在处理一个表单,用户输入一个日期范围并从复选框列表中选择一周中的一天/几天,即周日、周一、周二、周三、周四、周五和周六。
提交表单后,我需要一种方法来获取基于所选日期输入的两个日期之间的日期列表,即两个给定日期之间的所有星期一和星期四。我已经浏览了文档,但无法指出如何有效地做到这一点,即 ruby 方式。
有趣的一个!:D
start_date = Date.today # your start
end_date = Date.today + 1.year # your end
my_days = [1,2,3] # day of the week in 0-6. Sunday is day-of-week 0; Saturday is day-of-week 6.
result = (start_date..end_date).to_a.select {|k| my_days.include?(k.wday)}
使用上面的数据,您将获得从现在到明年的所有周一/周二/周三的数组。
另一种方法是将日期范围分组wday
并选择一周中的哪一天:
datesByWeekday = (start_date..end_date).group_by(&:wday)
datesByWeekday[0] # All Sundays
例如,要获得 2019 年 3 月的所有星期六:
> (Date.new(2019,03,01)..Date.new(2019,04,01)).group_by(&:wday)[0]
=> [Sun, 03 Mar 2019, Sun, 10 Mar 2019, Sun, 17 Mar 2019, Sun, 24 Mar 2019, Sun, 31 Mar 2019]
对速度很好奇,所以这就是我所做的。
这里有两种解决问题的方法:
1.week
一天直到停止对于范围解决方案,有多种使用范围的方法:
如何选择日期也很重要:
include?
在要求的日期运行我制作了一个文件来测试所有这些方法。我叫它test.rb
。我把它放在一个 Rails 应用程序的根目录下。我通过输入以下命令运行它:
rails c
load 'test.rb'
这是测试文件:
@days = {
'Sunday' => 0, 'Monday' => 1, 'Tuesday' => 2, 'Wednesday' => 3,
'Thursday' => 4, 'Friday' => 5, 'Saturday' => 6,
}
@start = Date.today
@stop = Date.today + 1.year
# use simple arithmetic to count number of weeks and then get all days by adding a week
def division(args)
my_days = args.map { |key| @days[key] }
total_days = (@stop - @start).to_i
start_day = @start.wday
my_days.map do |wday|
total_weeks = total_days / 7
remaining_days = total_days % 7
total_weeks += 1 if is_there_wday? wday, remaining_days, @stop
days_to_add = wday - start_day
days_to_add = days_to_add + 7 if days_to_add.negative?
next_day = @start + days_to_add
days = []
days << next_day
(total_weeks - 1).times do
next_day = next_day + 1.week
days << next_day
end
days
end.flatten.sort
end
def is_there_wday?(wday, remaining_days, stop)
new_start = stop - remaining_days
(new_start..stop).map(&:wday).include? wday
end
# take all the dates and group them by weekday
def group_by(args)
my_days = args.map { |key| @days[key] }
grouped = (@start..@stop).group_by(&:wday)
my_days.map { |wday| grouped[wday] }.flatten.sort
end
# run the select function on the range
def select_include(args)
my_days = args.map { |key| @days[key] }
(@start..@stop).select { |x| my_days.include? x.wday }
end
# run the select function on the range
def select_intersect(args)
my_days = args.map { |key| @days[key] }
(@start..@stop).select { |x| (my_days & [x.wday]).any? }
end
# take all the dates, convert to array, and then select
def to_a_include(args)
my_days = args.map { |key| @days[key] }
(@start..@stop).to_a.select { |k| my_days.include? k.wday }
end
# take all dates, convert to array, and check if interection is empty
def to_a_intersect(args)
my_days = args.map { |key| @days[key] }
(@start..@stop).to_a.select { |k| (my_days & [k.wday]).any? }
end
many = 10_000
Benchmark.bmbm do |b|
[[], ['Sunday'], ['Sunday', 'Saturday'], ['Sunday', 'Wednesday', 'Saturday']].each do |days|
str = days.map { |x| @days[x] }
b.report("#{str} division") { many.times { division days }}
b.report("#{str} group_by") { many.times { group_by days }}
b.report("#{str} select_include") { many.times { select_include days }}
b.report("#{str} select_&") { many.times { select_intersect days }}
b.report("#{str} to_a_include") { many.times { to_a_include days }}
b.report("#{str} to_a_&") { many.times { to_a_intersect days }}
end
end
排序结果
[] division 0.017671
[] select_include 2.459335
[] group_by 2.743273
[] to_a_include 2.880896
[] to_a_& 4.723146
[] select_& 5.235843
[0] to_a_include 2.539350
[0] select_include 2.543794
[0] group_by 2.953319
[0] division 4.494644
[0] to_a_& 4.670691
[0] select_& 4.897872
[0, 6] to_a_include 2.549803
[0, 6] select_include 2.553911
[0, 6] group_by 4.085657
[0, 6] to_a_& 4.776068
[0, 6] select_& 5.016739
[0, 6] division 10.203996
[0, 3, 6] select_include 2.615217
[0, 3, 6] to_a_include 2.618676
[0, 3, 6] group_by 4.605810
[0, 3, 6] to_a_& 5.032614
[0, 3, 6] select_& 5.169711
[0, 3, 6] division 14.679557
趋势
range.select
略快于range.to_a.select
include?
比intersect.any?
group_by
快于intersect.any?
但慢于include?
division
什么都没有给出时速度很快,但传递的参数越多,速度就会显着减慢结论
如果你结合select
和include?
,你有这个问题的最快和最可靠的解决方案