HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Ensure continuous date range

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
rangedateensurecontinuous

Problem

I have two arrays

dates = ["20161010","20161013"]
values = [5,3]


which represent dates and a view count. To display them in a graph, I also need the values between the dates, which are 0:

["20161010","20161011","20161012","20161013"]
[5,0,0,3]


I came up with a fairly complicated solution:

dates = ["20161010","20161013"]
values = [5,3]
first_date = Date.new(Integer(dates.first[0..3]), Integer(dates.first[4..5]), Integer(dates.first[6..7]))
last_date = Date.new(Integer(dates.last[0..3]), Integer(dates.last[4..5]), Integer(dates.last[6..7]))

# create a continous range
range = (first_date..last_date).to_a.map {|d| d.strftime("%Y%m%d")}
# store the indexes of the original dates inside the new range
date_idxs = dates.map {|d| range.index(d) }

# create a zero filled array with length of our range
filled_values = Array.new(range.length, 0)

values.each_with_index do |value,idx|
  idx_in_filled_values = date_idxs[idx]
  filled_values[idx_in_filled_values] = value
end

puts range # ["20161010", "20161011", "20161012", "20161013"]
puts filled_values # [5, 0, 0, 3]


I think thats not a good solution. Any ideas on how to improve the code? Or a completely different approach?

Solution

Several aspects of your code could use improvement:

  • It would be nice if the code were packaged in a function.



-
Date.new(Integer(dates.first[0..3]), Integer(dates.first[4..5]), Integer(dates.first[6..7])) is rather verbose and ugly.

I recommend DateTime.strptime for date parsing — it's the inverse of strftime.

  • You can call Range#map directly; you don't need to do to_a first.



-
dates.map { |d| range.index(d) } could be problematic for performance, since it is O(n2).

Instead, you should make a Hash.

Here is one solution:

require 'date_core'

DATE_FMT = '%Y%m%d'

def filled_date_series(dates, values, default_value=0)
  data = Hash[dates.zip(values)]
  datetime_range = (DateTime.strptime(dates.first, DATE_FMT) ..
                    DateTime.strptime(dates.last,  DATE_FMT))
  date_range = datetime_range.map { |dt| dt.strftime(DATE_FMT) }
  [
    date_range,
    date_range.map { |d| data[d] || default_value }
  ]
end

dates = ["20161010", "20161013"]
values = [5, 3]
range, filled_values = filled_date_series(dates, values)

Code Snippets

require 'date_core'

DATE_FMT = '%Y%m%d'

def filled_date_series(dates, values, default_value=0)
  data = Hash[dates.zip(values)]
  datetime_range = (DateTime.strptime(dates.first, DATE_FMT) ..
                    DateTime.strptime(dates.last,  DATE_FMT))
  date_range = datetime_range.map { |dt| dt.strftime(DATE_FMT) }
  [
    date_range,
    date_range.map { |d| data[d] || default_value }
  ]
end

dates = ["20161010", "20161013"]
values = [5, 3]
range, filled_values = filled_date_series(dates, values)

Context

StackExchange Code Review Q#147226, answer score: 3

Revisions (0)

No revisions yet.