2

My goal is to create an array on integers, each representing the days elapsed between two dates. Eventually I’ll average and do other operations on it.

I’ve reached working code:

require 'date'

dates = ['2020-01-30', '2020-01-24', '2020-01-16'].map { |d| Date.parse(d) }

day_difference = []

dates.each_index do |index|
  begin
    day_difference.push((dates[index] - dates[index + 1]).to_i)
  rescue TypeError # end of array
    break
  end
end

But I’m wondering if there’s a cleaner way, without having to look out for the last index value. Ruby arrays have a lot of methods, so I wouldn’t be surprised if one of those held a better solution.

user137369
  • 5,219
  • 5
  • 31
  • 54

2 Answers2

2

You can solve it in one loop with Enumerable#each_with_object and Enumerator#with_index methods.

dates = ['2020-01-30', '2020-01-24', '2020-01-16']

day_difference = dates.each_with_object([]).with_index do |(date, arr), index|
  next if index == dates.size - 1

  arr << (Date.parse(date) - Date.parse(dates[index + 1])).to_i
end
user137369
  • 5,219
  • 5
  • 31
  • 54
demir
  • 4,591
  • 2
  • 22
  • 30
1
require 'date'

dates = ['2020-01-30', '2020-01-24', '2020-01-16']

dates.map { |s| DateTime.strptime(s, '%Y-%m-%d').to_date }.
      each_cons(2).map { |d1,d2| (d1-d2).to_i }
  #=> [6, 8]

Change (d1-d2) to (d2-d1) if desired.

See Enumerable#each_cons.

One could map just once by writing

dates.each_cons(2).map { |s1,s2| (DateTime.strptime(s1, '%Y-%m-%d').to_date -
  DateTime.strptime(s2, '%Y-%m-%d').to_date).to_i }

but that has the disadvantage that strptime must be applied twice to dates.size-2 of the date strings.

Date#parse should only be used (instead of DateTime::strptime) if one has a high degree of confidence that the date strings will all be in the proper format. (Try
Date.parse("Parse may work or may not work").)

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100