Ruby: summing up values within an array of hashes between certain dates
I have an array of hashes in Ruby:
array = [
{:date => Wed, 04 May 2011 00:00:00 PDT -07:00,
:value => 200}
{:date => Wed, 04 May 2011 01:00:00 PDT -07:00,
:value => 100}
{:date => Tue, 03 May 2011 01:00:00 PDT -07:00,
:value => 300}
{:date => Tue, 03 May 2011 01:00:00 PDT -07:00,
:value => 150}
]
I'd like to be able to combine the values within each day so that I have a new array like this:
array = [
{:date => Wed, 04 May 2011 00:00:00 PDT -07:00,
:value => 300}
{:date => Tue, 03 May 2011 00:00:00 PDT -07:00,
:value => 450}
]
What's the most elegant way to search the array by day and sum up the values for each day?
This is what I initially tried:
entries = [
{:date => Wed, 04 May 2011 00:00:00 PDT -07:00,
:value => 200}
{:date => Wed, 04 开发者_运维知识库May 2011 01:00:00 PDT -07:00,
:value => 100}
{:date => Tue, 03 May 2011 01:00:00 PDT -07:00,
:value => 300}
{:date => Tue, 03 May 2011 01:00:00 PDT -07:00,
:value => 150}
]
first_day = 29.days.ago.beginning_of_day
total_days = 30
day_totals = (0...total_days).inject([]) do |array, num|
startat = first_day + num.day
endat = startat.end_of_day
total_value_in_day = entries.where("date >= ? and date <= ?", startat, endat).sum(:value)
array << {:date => startat, :value => total_value_in_day}
end
I realized my mistake was with the where
method which is a Rails method for searching objects, and can't be used on arrays. So my main question, is there a way to search arrays or hashes with conditions.
You can iterate over the entries to create a new array:
totals = Hash.new(0)
array.each do |entry|
totals[entry[:date]] += entry[:value]
end
# Now totals will be something like this:
# => {"Wed, 04 May 2011" => 300, "Tue, 03 May 2011" => 450...}
# If you then want this in the same array format you started with:
new_array = totals.collect{ |key, value| {:date => key, :value => value} }
# => [{:date => "Wed, 04 May 2011", :value => 300}, {....}]
For 1.9.2:
>> array.each_with_object(Hash.new(0)) { |el, hash| hash[el[:date]] += el[:value] }
#=> {"Wed, 04 May 2011 00:00:00 PDT -07:00"=>200, "Wed, 04 May 2011 01:00:00 PDT -07:00"=>100, "Tue, 03 May 2011 01:00:00 PDT -07:00"=>450}
Also works with 1.8:
>> array.inject(Hash.new(0)) { |hash, el| hash[el[:date]] += el[:value] ; hash }
#=> {"Wed, 04 May 2011 00:00:00 PDT -07:00"=>200, "Wed, 04 May 2011 01:00:00 PDT -07:00"=>100, "Tue, 03 May 2011 01:00:00 PDT -07:00"=>450}
精彩评论