How do I add values in an array when there is a null entry?
I want to create a real time-series array. Currently, I am using the statistics gem to pull out values for each 'day':
define_statistic :sent_count, :count
=> :all, :group => 'DATE(date_sent)',
:filter_on => {:email_id => 'email_id
> = ?'}, :order => 'DATE(date_sent) ASC'
What this does is create an array where there are values for a date, for example
[["12-20-2010",1], ["12-24-2010",3]]
But I need it to fill in the null values, so it looks more like:
[["12-20-2010",1], ["12-21-2010",0], ["12-22-2010",0], ["12-23-开发者_开发知识库2010",0], ["12-24-2010",3]]
Notice how the second example has "0" values for the days that were missing from the first array.
#!/usr/bin/ruby1.8
require 'date'
require 'pp'
def add_missing_dates(series)
series.map do |date, value|
[Date.strptime(date, '%m-%d-%Y'), value]
end.inject([]) do |series, date_and_value|
filler = if series.empty?
[]
else
((series.last[0]+ 1)..(date_and_value[0] - 1)).map do |date|
[date, 0]
end
end
series + filler + [date_and_value]
end.map do |date, value|
[date.to_s, value]
end
end
a = [["12-20-2010",1], ["12-24-2010",3]]
pp add_missing_dates(a)
# => [["2010-12-20", 1],
# => ["2010-12-21", 0],
# => ["2010-12-22", 0],
# => ["2010-12-23", 0],
# => ["2010-12-24", 3]]
I would recommend against monkey-patching the base classes to include this method: It's not all that general purpose; even if it were, it just doesn't need to be there. I'd stick it in a module that you can mix in to whatever code needs it:
module AddMissingDates
def add_missing_dates(series)
...
end
end
class MyClass
include AddMissingDates
...
end
However, if you really want to:
def Array.add_missing_dates(series)
...
end
This works:
#!/usr/bin/env ruby
require 'pp'
require 'date'
# convert the MM-DD-YYYY format date string to a Date
DATE_FORMAT = '%m-%d-%Y'
def parse_date(s)
Date.strptime(s, DATE_FORMAT)
end
dates = [["12-20-2010",1], ["12-24-2010",3]]
# build a hash of the known dates so we can skip the ones that already exist.
date_hash = Hash[*dates.map{ |i| [parse_date(i[0]), i[-1]] }.flatten]
start_date_range = parse_date(dates[0].first)
end_date_range = parse_date(dates[-1].first)
# loop over the date range...
start_date_range.upto(end_date_range) do |d|
# ...and adding entries for the missing ones.
date_hash[d] = 0 if (!date_hash.has_key?(d))
end
# convert the hash back into an array with all dates
all_dates = date_hash.keys.sort.map{ |d| [d.strftime(DATE_FORMAT), date_hash[d] ] }
pp all_dates
# >> [["12-20-2010", 1],
# >> ["12-21-2010", 0],
# >> ["12-22-2010", 0],
# >> ["12-23-2010", 0],
# >> ["12-24-2010", 3]]
Most of the code is preparing things, either to build a new array, or return the date objects back to strings.
精彩评论