26

ユーザーが日付範囲を入力し、チェックボックスのリストから日/曜日、つまり日曜日、月曜日、火曜日、水曜日、木曜日、金曜日、土曜日を選択するフォームに取り組んでいます。

フォームが送信されたら、選択された日に基づいて、入力された 2 つの日付の間の日付のリストを取得する方法が必要です。つまり、指定された 2 つの日付の間のすべての月曜日と木曜日です。私はドキュメントに目を通しましたが、これを効率的に行う方法、つまりルビーの方法を特定することはできません。

4

3 に答える 3

65

楽しいもの!: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)}

上記のデータを使用すると、現在から来年までのすべての月/火/水の配列が得られます。

于 2012-05-03T10:12:40.130 に答える
16

もう 1 つの方法は、日付範囲をグループ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]

https://apidock.com/ruby/Date/wday

于 2016-03-06T18:29:53.613 に答える
5

速度に興味があったので、これが私がしたことです。

この問題を解決するには、次の 2 つの方法があります。

  • 範囲とフィルターを使用する
  • 最初の日を見つけ1.weekて、停止するまでその日に追加します

範囲ソリューションの場合、範囲を使用するさまざまな方法があります。

  • すべての日付を取得し、平日ごとにグループ化します
  • 範囲で選択関数を実行します
  • 範囲を配列に変換してから選択を実行します

日付の選択方法も重要です。

  • include?リクエストされた日に実行
  • 2 つの配列の交点を見つけて空かどうかを確認する

これらすべてのメソッドをテストするためのファイルを作成しました。私はそれを呼んだ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何も指定されていない場合は高速ですが、渡されるパラメーターが多いほど大幅に遅くなります

結論

と を組み合わせるselectinclude?、この問題に対する最速かつ最も信頼性の高いソリューションが得られます。

于 2018-04-10T19:59:08.083 に答える