3
\$\begingroup\$

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?

asked Nov 16, 2016 at 18:45
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

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)
answered Nov 16, 2016 at 19:44
\$\endgroup\$
3
\$\begingroup\$

Some notes:

  • dates.first[0..3]. Use Date.parse or Date.strptime.

  • Array.new(range.length, 0). As a general rule, don't use that imperative style to create arrays. Use enumerable methods instead to build expressions.

  • Is your code inside a class or module or at least a function?

I'd write it in functional style:

require 'date'
string_dates = ["20161010", "20161013"]
values = [5, 3]
start_date = Date.parse(string_dates.first)
end_date = Date.parse(string_dates.last)
values_by_date = string_dates.zip(values).to_h
range = (start_date..end_date).map { |date| date.strftime("%Y%m%d") }
filled_values = range.map { |date| values_by_date.fetch(date, 0) }
answered Nov 16, 2016 at 19:45
\$\endgroup\$
3
  • \$\begingroup\$ The example has just two dates and two values, but the code should work for multiple data points. \$\endgroup\$ Commented Nov 16, 2016 at 19:47
  • \$\begingroup\$ Edited. Well, the result, unsurprisingly, is not very different than your answer, not sure if it makes sense to keep it. \$\endgroup\$ Commented Nov 16, 2016 at 19:57
  • 1
    \$\begingroup\$ The general idea is the same, but there are some differences in the details that are worth showing, such as Date.parse, .to_h, and .fetch. \$\endgroup\$ Commented Nov 16, 2016 at 20:00

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.