2014-01-29 3 views
4

Я хочу взять список datetimes в Ruby, а затем рассчитать количество дней между их, а затем в среднем эти даты. Это верно? Есть ли более короткий способ сделать это?Рассчитать среднее количество дней между датами в Ruby

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 

И будет ли это выводить его в формате дней?

Вы также можете указать мне версию Rails, если хотите, поскольку я буду тянуть время от поля модели.

+1

Ваш вопрос будет лучше, если в массиве «даты» содержатся фактические даты в общем формате. Наверное, трех будет достаточно. Например: 'date = [" 21 ноября 2013 "," 22 декабря 2013 года "," 20 января 2014 года "]' Затем покажите вывод, который вы получите для этой даты. Это также позволит читателям проверить код, который они могут предложить. –

+0

Хорошо спасибо. Я добавил некоторые даты. – user2270029

+0

Вы также можете использовать замечательную функцию 'Time.parse()' Ruby's! Обычно это может выяснить, как автоматически преобразовать строку в объект 'Time'. – TrinitronX

ответ

3

Я думаю, что у вас есть офф-по -одна проблема. Если у вас есть n значения в dates, то вы будете иметь n-1 различия между последовательными парами так что ваш avg должно быть:

avg = difference.inject{ |sum, el| sum + el }.to_f/(arr.size - 1) 

И ваш inject просто долго наматывается способ сказать inject(:+), чтобы вы могли сказать:

avg = difference.inject(:+).to_f/(arr.size - 1) 

в любом случае, вы могли бы использовать map и each_cons, чтобы сделать его немного лучше:

dates.map(&:to_date).each_cons(2).map { |d1, d2| d1 - d2 }.inject(:+)/(dates.length - 1) 

Сначала мы можем конвертировать все в датах за один проход с map(&:to_date), а не конвертировать каждый раз, когда нам нужна дата вместо строки. Затем each_cons(2) выполняет итерацию по массиву в последовательных парах (то есть [1,2,3,4] итерируется как [[1,2], [2,3], [3,4]]). Затем просто введите map, чтобы получить разницу между парами и inject(:+), чтобы добавить их все.

Вышеупомянутые останутся у вас с Rational, но вы можете to_f или to_i, если вам нужна плавающая или целая величина.

+0

Это чисто удивительно. – Micah

0

А что-то вроде:

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 

или если я понял неправильно, то есть всегда:

span_secs = Foo.maximum(:created_at) - Foo.minimum(:created_at) 
avg_secs = span_secs/(Foo.count - 1) 
avg_days = avg_secs/(24 * 60 * 60) 

См: Calculating the average days between records in Rails

+0

Рассчитывает ли это количество дней между каждой датой, а затем среднее значение? Может быть, я что-то упустил. – user2270029

1

Я мог бы быть выключен здесь, но я думаю, что это должно работать:

require 'date' 
dates.map!{|x| Date.parse(x)}.sort! 
p (dates.last - dates.first)/(dates.size - 1) #=> (142/1) 

или это более красиво?

first, last = dates.map!{ |x| Date.parse(x) }.minmax 
p (last - first)/(dates.size - 1) #=> (142/1) 
+0

Nice и чистый, hiro. –

+0

Пока даты не меняют направление в конечных точках; например, '% w [2012-08-05 2012-06-17 2012-06-15 2011-06-06 2011-06-20]' будет проблематичным, так как изменение знака в последнем различии отменяет неявные отмены в мин/макс. –

Смежные вопросы