Get integers for all months between two dates

I have the following method which should work in Rails, but I am not sure if this is the best implementation. It looks a little clumsy, given all the good helpers in rails. Am I missing a much better way to do this?

def get_months_and_years(st,ed)     m = [st.month]     d = st.beginning_of_month     while d <= ed.beginning_of_month       m << [date.month, date.year]       d = d.next_month     end     return m end

Thanks,

Tim

No doubt there is a slicker way, but I suggest that maybe the thing that is a little clumsy is code that needs this functionality in the first place ( I mean building the array of numbers ). Can you not just keep the start and end dates and determine the month and year values at the time you need them (if you really need them at all that is).

Colin

I would do it this way:

def get_months_and_year(st, ed) m = if ed >= st years = ed.year - st.year if years == 0 (st.month…ed.month).each{|month| m << [month, st.year] } elsif years > 0 (st.month…12).each{|month| m << [month, st.year] } (st.year+1…ed.year-1).each{|year| (1…12).each{|month| m << [month, year] } } (1…ed.month).each{|month| m << [month, ed.year] } end end return m end

Just for not processing date objects many times. This code would be refactored.

Daniel Alejandro Gaytán Valencia

Colin,

Good point, but I am not sure of another way. I need to display calendars that show all events in a group. I'll look at the bigger picture again.

Best,

tim

Colin Law wrote:

Not sure exactly what you are doing of course, but could you write view helper code that takes the start and end dates and generates the appropriate view stuff?

Colin

As a couple of people have said, looking at this in isolation won't allow us to easily give suggestions of better solutions, but you could certainly tidy that method a little.

Firstly, (personal preference time!) your variable names aren't helping your code be "self documenting". Even in such a small method, it's frustrating to try to remember what all those single-character variables are supposed to be doing. If they're named to refer to what they do, they will help other people (or you when you come back to it in a couple of months!) understand what the intention behind their existence is.

Every time you loop round, you calculate "st.beginning_of_month" - it would be a bit more efficient to store the value for use in the comparison each time. You never use "st" again, so I'd just overwrite it.

The first element you populate the array with only has "st.month", all the others have the year too - not sure if this is a typo or intended? Another potential typo is in the array push; you refer to a "date" variable which I can't see set anywhere. I assume this is actually your "d" variable?

So, if I were writing your method to be clearer, it might look like this:

  def get_months_and_years(start_date, end_date)     end_date = end_date.beginning_of_month return_value = [start_date.month]

    working_date = start_date.beginning_of_month while working_date <= end_date return_value << [working_date.month, working_date.year] working_date = working_date.next_month end return return_value   end

But.... My inclination would be to be a bit more "Ruby"... assuming you want an array of arrays of month and year for every month between two dates:

  def get_months_and_years(start_date, end_date)     # it would be worth checking that the parameters are both valid dates/times     (start_date..end_date).collect { date| [date.month, date.year] }.uniq   end

In that method I'm just taking the month and year for every date in the range, and then returning unique values from the collection (so one for each month/year combination. I've not got any time to look into it, but I wonder if the Range.collect can step through month-at-a-time rather than day-at-a-time...

I don't know if it's any quicker or much slower, but it's certainly more concise, and (to my eye) a bit clearer in its intention, and looks a lot less like PHP :slight_smile:

HTH Michael