4

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.

5
  • The most elegant way really is for you to write some code yourself, if you don't mind my being elegantly blunt. Elegance in code is a value, and it is good to reach for it, but you cannot reach mastery without (painful) exercise. Perhaps read a good book Commented May 4, 2011 at 20:44
  • edited the post to show the code I tried on my own, and where I was stuck. Commented May 4, 2011 at 20:55
  • Where does the combined value 450 for Tue 03 May come from? Commented May 4, 2011 at 20:58
  • Aha, much better question now ! Commented May 4, 2011 at 20:58
  • @sawa, oops that was a typo, just fixed Commented May 4, 2011 at 20:59

2 Answers 2

5

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}, {....}]
Sign up to request clarification or add additional context in comments.

1 Comment

This worked great! I also had to use entry[:date].beginning_of_day to add up entries within varying times of the day. Thanks!
3

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}

Comments

Your Answer

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.