I am trying to loop through years and months. If @user.created_at is November 2015, then I'd like to get this output:
November 2015
December 2015
January 2016
February 2016
March 2016
I tried the following:
Hash[(@user.created_at.month..Date.today.month).map { |month| [ month, 0 ] }].merge(...)
But the above code returns
stack level too deep
because it loops 11..3.
How can I figure out this issue?
If you want to compute the upcoming months/years you could always use advance:
start = @user.created_at.to_date.beginning_of_month
Hash[(0..6).collect { |n| [ start.advance(months: n).month, 0 ] }]
That should properly step through days/months. You may want to just stick in dates instead of just the month number.
If you want to do "up to today" then try this:
date = @user.created_at.to_date.beginning_of_month
stop = Date.today.beginning_of_month
hash = { }
while (date <= stop)
hash[date] = 0
date = date.advance(months: 1)
end
One thing you can do is turn each date into a "month number" that's the year multiplied by 12 plus the zero-based month, and then iterate over them, e.g.:
from_date = Date.new(2015, 11) # Nov. 1, 2015
to_date = Date.today # Mar. 22, 2016
nov2015 = from_date.year * 12 + (from_date.month - 1)
# => 24190
nov2015.divmod(12)
# => [2015, 10]
mar2016 = to_date.year * 12 + (to_date.month - 1)
# => 24194
mar2016.divmod(12)
# => [2016, 2]
Remember that the months are zero-based, so 2 is March and 10 is November. As expected, nov2015 and mar2016 have a difference of four. Now we can iterate from one to the other:
nov2015.upto(mar2016) do |month_num|
year, month = month_num.divmod(12)
puts Date.new(year, month + 1).strftime("%B %Y")
end
# => November 2015
# December 2015
# January 2016
# February 2016
# March 2016
Perfect! But it's better if we can put this into a method that returns an Enumerator so we can use any of the Enumerable methods (each, map, each_cons, you name it) on it:
def months_enum(from_date, to_date)
from = from_date.year * 12 + from_date.month - 1
to = to_date.year * 12 + to_date.month - 1
Enumerator.new do |y|
from.upto(to) do |n|
year, month = n.divmod(12)
y << Date.new(year, month + 1)
end
end
end
Then:
from = Date.new(2015, 11, 1)
to = Date.today
months_enum(from, to).each do |date|
puts date.strftime("%Y-%m")
end
# -> 2015-11
# 2015-12
# 2016-01
# 2016-02
# 2016-03
p months_enum(from, to).map {|date| date.strftime("%B %Y") }
# => [ "November 2015",
# "December 2015",
# "January 2016",
# "February 2016",
# "March 2016" ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With