views:

141

answers:

2

I have a date range that I would like to be able to loop through in reverse. Give the following, how would I accomplish this, the standard Range operator doesn't seem t be working properly.

>> sd = Date.parse('2010-03-01')
=> Mon, 01 Mar 2010
>> ed = Date.parse('2010-03-05')
=> Fri, 05 Mar 2010
>> (sd..ed).to_a
=> [Mon, 01 Mar 2010, Tue, 02 Mar 2010, Wed, 03 Mar 2010, Thu, 04 Mar 2010, Fri, 05 Mar     2010]
>> (ed..sd).to_a
=> []

as you can see, the range operator works properly form start to end, but not from end to start.

+5  A: 

Try upto/downto:

irb(main):003:0> sd = Date.parse('2010-03-01')
=> #<Date: 4910513/2,0,2299161>
irb(main):004:0> ed = Date.parse('2010-03-15')
=> #<Date: 4910541/2,0,2299161>
irb(main):005:0> sd.upto(ed) { |date| puts date }
2010-03-01
2010-03-02
2010-03-03
2010-03-04
2010-03-05
2010-03-06
2010-03-07
2010-03-08
2010-03-09
2010-03-10
2010-03-11
2010-03-12
2010-03-13
2010-03-14
2010-03-15
=> #<Date: 4910513/2,0,2299161>
irb(main):006:0> ed.downto(sd) { |date| puts date }
2010-03-15
2010-03-14
2010-03-13
2010-03-12
2010-03-11
2010-03-10
2010-03-09
2010-03-08
2010-03-07
2010-03-06
2010-03-05
2010-03-04
2010-03-03
2010-03-02
2010-03-01
=> #<Date: 4910541/2,0,2299161>
Joe
If you're lazy you can also simply use .collect.reverse which works just as well under most circumstances.
tadman
except that then it has to build the whole array first then reverse it. upto and downto are enumerators.
glenn jackman
+1  A: 

I usually just reverse the resulting array:

ruby-1.8.7-p72 > sd = Date.parse('2010-03-01')
 => Mon, 01 Mar 2010 
ruby-1.8.7-p72 > ed = Date.parse('2010-03-05')
 => Fri, 05 Mar 2010 
ruby-1.8.7-p72 > (sd..ed).to_a
 => [Mon, 01 Mar 2010, Tue, 02 Mar 2010, Wed, 03 Mar 2010, Thu, 04 Mar 2010, Fri, 05 Mar 2010] 
ruby-1.8.7-p72 > (sd..ed).to_a.reverse
 => [Fri, 05 Mar 2010, Thu, 04 Mar 2010, Wed, 03 Mar 2010, Tue, 02 Mar 2010, Mon, 01 Mar 2010] 

I guess, to make it do the right thing when you don't know if the start date is going to be before or after the end date, you'd want something along the lines of:

def date_range(sd, ed)
  sd < ed ? (sd..ed).to_a : (ed..sd).to_a.reverse
end

which will give you the right thing either way:

ruby-1.8.7-p72 > sd = Date.parse('2010-03-01')
 => Mon, 01 Mar 2010 
ruby-1.8.7-p72 > ed = Date.parse('2010-03-05')
 => Fri, 05 Mar 2010 
ruby-1.8.7-p72 > date_range(sd, ed)
 => [Mon, 01 Mar 2010, Tue, 02 Mar 2010, Wed, 03 Mar 2010, Thu, 04 Mar 2010, Fri, 05 Mar 2010] 
ruby-1.8.7-p72 > date_range(ed, sd)
 => [Fri, 05 Mar 2010, Thu, 04 Mar 2010, Wed, 03 Mar 2010, Tue, 02 Mar 2010, Mon, 01 Mar 2010] 
Graeme Mathieson
i like this much better!
Joe