Reputation: 6541
I have some data for a some graphs arranged in an array of arrays that looks like:
[[date1, value1], [date2, value2], [date3, value3]]
i.e. [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
My problem is that my graphing utility isn't graphing any data for "6-02-13", while I would like it to graph 0.
I have another array of all the valid dates, e.g. ["6-01-13", "6-02-13", "6-03-13", ...]
What's the best way to insert [date, 0]
into my data array for all dates that aren't already present in my data array?
I don't care about the array's ordering.
I figured I'd do something along the lines of:
dates_array.each do |date|
unless data_array.has_date(date)
data_array.push([date, 0])
end
end
But I can't think of how this has_date(date) method should work without looping through all the dates and checking that that date is represented in my data array (which would naively be a loop of loops and therefore not ideal).
edit: Existing data (and dates) are pulled from the database as arrays.
Upvotes: 2
Views: 2980
Reputation: 3298
An alternative without using looping
dates_present = data.map(&:first)
dates_missing = dates_array - dates_present
data += dates_missing.map { |date| [date, 0] }
Upvotes: 1
Reputation: 1905
Convert your array of arrays into a hash with a default value of zero:
def data_to_hash(data)
Hash.new(0).merge(Hash[data])
end
data = [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
hashed_data = data_to_hash(data)
p hashed_data['6-02-13']
p hashed_data['6-01-13']
Output:
0
5
Any date not in the array will return 0. Using a hash as your data structure is much faster on large data sets than iterating through the array looking for a date.
Answer update
To make the invalid keys "stick" to the hash, the block variant of Hash#new may be used:
def data_to_hash(data)
Hash.new { |h,k| h[k] = 0 }.merge(Hash[data])
end
data = [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
hashed_data = data_to_hash(data)
p hashed_data
%w{6-03-13 7-8-99}.each do |d|
p hashed_data[d]
end
p hashed_data
Output:
{"6-01-13"=>5, "6-03-13"=>2, "6-04-13"=>11}
2
0
{"6-01-13"=>5, "6-03-13"=>2, "6-04-13"=>11, "7-8-99"=>0}
In this example, 7-8-99 is not present in the original data set, but is set to 0 when that key is accessed.
Upvotes: 5
Reputation: 114138
data = [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
valid_dates = ["6-01-13", "6-02-13", "6-03-13"]
data + ( valid_dates - data.map(&:first) ).map { |d| [d, 0] }
#=> [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11], ["6-02-13", 0]]
data.map(&:first)
returns just the dates, i.e. ["6-01-13", "6-03-13", "6-04-13"]
valid_dates - …
calculates the difference, i.e. the missing dates ["6-02-13"]
.map { |d| [d, 0] }
converts these into [<date>, 0]
pairsdata + …
concatenates the data array and the missing date pairs arrayUpvotes: 0
Reputation: 5803
Make sure you get your data sorted from the database, or dates_array.sort!{|a,b| Date.parse(a[0]) <=> Date.parse(b[0])}
(Date.parse(dates_array.first[0])..Date.parse(dates_array.last[0])).collect do |date|
dates_array.find{|i| i[0] == date} || [date, 0]
end
I didn't like the accepted answer b/c it requires an existing array.
Upvotes: 0
Reputation: 118261
You can use the below strategy :
dates_array = [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
dates_array.push(["16-03-13",0]) unless dates_array.find{|i,j| i == "16-03-13"}
dates_array # => [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11], ["16-03-13", 0]]
Here is the complete approach:
def date_check(arr,date)
arr.push(["16-03-13",0]) unless arr.find{|i,j| i == date}
end
dates_array = [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11]]
date_check(dates_array,"16-03-13") # => [["6-01-13", 5], ["6-03-13", 2], ["6-04-13", 11], ["16-03-13", 0]]
date_check(dates_array,"6-01-13") # => nil
Upvotes: 0
Reputation: 4798
If you know your date ranges, you can just pre-fill an array with all the dates of that range and provide a default value of zero to them.
array = Array.new(31) {['date', 0]}
Just provide actual info instead of date.
Or do it this way: (date..date+31).to_a.map!(&:to_s).zip([0]*32)
If you provide me with the way you get existing date items, I'd make something more suitable for you, I suppose.
Upvotes: 1
Reputation: 282
dates_array.each do |date|
data.push [date, 0] unless data.map(&:first).include? date
end
This works. Perhaps someone can improve upon it.
Upvotes: 1