Creating an array of the difference between items in another array

Viewed 42

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.

2 Answers

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
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").)

Related