如何在Ruby中的两个日期之间获得所有星期日?

时间:2022-08-03 21:31:05

I'm working on a form where the user enters a date range and selects from a list of checkboxes a day/days of the week i.e Sunday, Monday, Tuesday, Wednesday, Thursday, Friday and saturday.

我正在处理一个用户输入日期范围的表格,并从一周中的一天/几天的复选框列表中选择,即周日,周一,周二,周三,周四,周五和周六。

Once the form is submitted I need a way to grab a list of dates between the two dates entered based upon the days that were chosen i.e All Mondays and Thursdays between the two dates given. I've looked through the docs but can't pin point how to do this efficiently i.e the ruby way.

一旦表格提交,我需要一种方法来获取根据所选日期输入的两个日期之间的日期列表,即所给出的两个日期之间的所有星期一和星期四。我查看了文档,但无法确定如何有效地执行此操作,即红宝石方式。

3 个解决方案

#1


47  

fun one! :D

有趣的一个! :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)}

using the data above you'll get an array of all Mon/Tue/Weds between now and next year.

使用上面的数据,您将获得从现在到明年之间所有周一/周二/周二的数组。

#2


4  

Another approach is to group your date range by wday and pick off your day of the week:

另一种方法是按日期对日期范围进行分组,然后选择一周中的某一天:

datesByWeekday = (start_date..end_date).group_by(&:wday)
datesByWeekday[6] # All Sundays

for example, to get all Sundays in March 2016:

例如,要在2016年3月获得所有星期日:

> (Date.new(2016,03,01)..Date.new(2016,04,01)).group_by(&:wday)[6]
=> [Sat, 05 Mar 2016, Sat, 12 Mar 2016, Sat, 19 Mar 2016, Sat, 26 Mar 2016] 

#3


0  

Was curious about speed, so here's what I did.

对速度感到好奇,所以这就是我所做的。

Here are two approaches to solve the problem:

以下是解决问题的两种方法:

  • Use a range and filter
  • 使用范围和过滤器
  • Find first day and add 1.week to that day until stop
  • 找到第一天并添加1.week到那一天直到停止

For the ranged solutions, there are different ways to use the range:

对于远程解决方案,有不同的方法可以使用该范围:

  • Take all the dates and group them by weekday
  • 记下所有日期并按工作日分组
  • Run the select function on the range
  • 运行范围上的选择功能
  • Convert range to array and then run select
  • 将范围转换为数组,然后运行select

How you select dates is also important:

如何选择日期也很重要:

  • Run include? on the days requested
  • 运行包含?在要求的日子里
  • Find intersection between two arrays and check if empty
  • 找到两个数组之间的交集并检查是否为空

I made a file to test all these methods. I called it test.rb. I placed it at the root of a rails application. I ran it by typing these commands:

我做了一个文件来测试所有这些方法。我叫它test.rb.我把它放在rails应用程序的根目录下。我通过输入以下命令来运行它:

  • rails c
  • 铁轨
  • load 'test.rb'
  • 加载'test.rb'

Here's the testing file:

这是测试文件:

@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

Sorted results

排序结果

[] 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

Trends

趋势

  • range.select is slightly faster than range.to_a.select
  • range.select比range.to_a.select略快
  • include? is faster than intersect.any?
  • 包括?比intersect.any更快?
  • group_by is faster than intersect.any? but slower than include?
  • group_by比intersect.by更快?但比包括慢?
  • division is fast when nothing is given, but slows down significantly the more params that are passed
  • 当没有给出任何东西时,除法很快,但是通过的越多的params显着减慢

Conclusion

结论

If you combine select and include?, you have the fastest and most reliable solution for this problem

如果将select和include?组合在一起,则可以找到解决此问题的最快,最可靠的解决方案

#1


47  

fun one! :D

有趣的一个! :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)}

using the data above you'll get an array of all Mon/Tue/Weds between now and next year.

使用上面的数据,您将获得从现在到明年之间所有周一/周二/周二的数组。

#2


4  

Another approach is to group your date range by wday and pick off your day of the week:

另一种方法是按日期对日期范围进行分组,然后选择一周中的某一天:

datesByWeekday = (start_date..end_date).group_by(&:wday)
datesByWeekday[6] # All Sundays

for example, to get all Sundays in March 2016:

例如,要在2016年3月获得所有星期日:

> (Date.new(2016,03,01)..Date.new(2016,04,01)).group_by(&:wday)[6]
=> [Sat, 05 Mar 2016, Sat, 12 Mar 2016, Sat, 19 Mar 2016, Sat, 26 Mar 2016] 

#3


0  

Was curious about speed, so here's what I did.

对速度感到好奇,所以这就是我所做的。

Here are two approaches to solve the problem:

以下是解决问题的两种方法:

  • Use a range and filter
  • 使用范围和过滤器
  • Find first day and add 1.week to that day until stop
  • 找到第一天并添加1.week到那一天直到停止

For the ranged solutions, there are different ways to use the range:

对于远程解决方案,有不同的方法可以使用该范围:

  • Take all the dates and group them by weekday
  • 记下所有日期并按工作日分组
  • Run the select function on the range
  • 运行范围上的选择功能
  • Convert range to array and then run select
  • 将范围转换为数组,然后运行select

How you select dates is also important:

如何选择日期也很重要:

  • Run include? on the days requested
  • 运行包含?在要求的日子里
  • Find intersection between two arrays and check if empty
  • 找到两个数组之间的交集并检查是否为空

I made a file to test all these methods. I called it test.rb. I placed it at the root of a rails application. I ran it by typing these commands:

我做了一个文件来测试所有这些方法。我叫它test.rb.我把它放在rails应用程序的根目录下。我通过输入以下命令来运行它:

  • rails c
  • 铁轨
  • load 'test.rb'
  • 加载'test.rb'

Here's the testing file:

这是测试文件:

@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

Sorted results

排序结果

[] 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

Trends

趋势

  • range.select is slightly faster than range.to_a.select
  • range.select比range.to_a.select略快
  • include? is faster than intersect.any?
  • 包括?比intersect.any更快?
  • group_by is faster than intersect.any? but slower than include?
  • group_by比intersect.by更快?但比包括慢?
  • division is fast when nothing is given, but slows down significantly the more params that are passed
  • 当没有给出任何东西时,除法很快,但是通过的越多的params显着减慢

Conclusion

结论

If you combine select and include?, you have the fastest and most reliable solution for this problem

如果将select和include?组合在一起,则可以找到解决此问题的最快,最可靠的解决方案