Reputation: 871
I want to take a list of datetimes in Ruby and then calculate the number of days between them and then average those dates. Is this correct? Is there a shorter way to do this?
dates = ["2012-08-05 00:00:00 UTC", "2012-06-17 00:00:00 UTC", "2012-06-15 00:00:00 UTC", "2011-06-06 00:00:00 UTC"]
difference = Array.new
dates.each_with_index do |date, index|
if index != 0
difference << date.to_date - dates[index - 1].to_date
end
end
avg = difference.inject{ |sum, el| sum + el }.to_f / arr.size
And then would that output it in days format?
You can give me a Rails version as well if you'd like as I'll be pulling the datetimes from a model field.
Upvotes: 4
Views: 1290
Reputation: 434665
I think you have an off-by-one problem. If you have n
values in dates
then you'll have n-1
differences between consecutive pairs so your avg
should be:
avg = difference.inject{ |sum, el| sum + el }.to_f / (arr.size - 1)
And your inject
is just a long winded way of saying inject(:+)
so you could say:
avg = difference.inject(:+).to_f / (arr.size - 1)
In any case, you could use map
and each_cons
to make it a bit nicer:
dates.map(&:to_date).each_cons(2).map { |d1, d2| d1 - d2 }.inject(:+) / (dates.length - 1)
First we can convert everything to dates in one pass with map(&:to_date)
rather than converting every time we need a date instead of a string. Then the each_cons(2)
iterates over the array in consecutive pairs (i.e. [1,2,3,4]
is iterated as [[1,2], [2,3], [3,4]]
). Then a simple map
to get the differences between the pairs and inject(:+)
to add them all up.
The above will leave you with a Rational
but you can to_f
or to_i
that if you need a floating point or integer value.
Upvotes: 5
Reputation: 13901
I might be off here, but i think this should work:
require 'date'
dates.map!{|x| Date.parse(x)}.sort!
p (dates.last - dates.first)/(dates.size - 1) #=> (142/1)
or is this more beautiful?
first, last = dates.map!{ |x| Date.parse(x) }.minmax
p (last - first)/(dates.size - 1) #=> (142/1)
Upvotes: 2
Reputation: 27214
What about something like:
def room_expiry_date_in_days(room)
a=room.expiry_date.strftime("%Y-%m-%d")
b=room.created_at.strftime("%Y-%m-%d")
a=Date.parse(a)
b=Date.parse(b)
days=(a-b).to_i
return "#{pluralize(days, 'day',"days")} left"
end
or if I've understood incorrectly, then there's always:
span_secs = Foo.maximum(:created_at) - Foo.minimum(:created_at)
avg_secs = span_secs / (Foo.count - 1)
avg_days = avg_secs / (24 * 60 * 60)
See: Calculating the average days between records in Rails
Upvotes: 0