1

Basically I have an array of hashes like so :

[
  { :id => 20, :total => 1, :total2 => 0 },
  { :id => 21, :total => 1, :total2 => 0 },
  { :id => 22, :total => 2, :total2 => 0 },
  { :id => 23, :total => 1, :total2 => 0 },
  { :id => 20, :total => 1, :total2 => 0 },
  { :id => 21, :total => 1, :total2 => 0 },
  { :id => 22, :total => 1, :total2 => 1 },
  { :id => 23, :total => 1, :total2 => 0 }
]

I want the array to sum the last two hash columns like so, keeping the first (:id) as an identifier:

[
  { :id => 20, :total => 2, :total2 => 0 },
  { :id  => 21, :total => 2, :total2 => 0 },
  { :id  => 22, :total => 3, :total2 => 1 }
]

I have looked around and it seems that the .inject() method is used in this instance but I cannot really figure out the syntax/how to use this.

What I am looking for is to keep the first column (:id) as an ID field; if there is another hash with this ID, like in my example above, the two hashes should be added together.

5 Answers 5

1

Can you try this?

array = [{:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0},{:stemp=>22, :vtotal=>2, :avg=>0}, {:stemp=>23, :vtotal=>1, :avg=>0},  {:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0}, {:stemp=>22, :vtotal=>1, :avg=>1}, {:stemp=>23, :vtotal=>1, :avg=>0}]

result = array.group_by{|h| h[:stemp]}.map do |stemp, hashes|
  { stemp: stemp, vtotal: hashes.map{|h| h[:vtotal]}.inject(:+), avg: hashes.map{|h| h[:avg]}.inject(:+) }
end

Just copy-pasted it in the IRB console with ruby 1.9.3, outputs this:

[
  {:stemp=>20, :vtotal=>2, :avg=>0},
  {:stemp=>21, :vtotal=>2, :avg=>0},
  {:stemp=>22, :vtotal=>3, :avg=>1},
  {:stemp=>23, :vtotal=>2, :avg=>0}
] 
Sign up to request clarification or add additional context in comments.

Comments

0

I reformatted the data in the question and in my answer so that it is a little easier for others to see what is going on.

data = [
  { :stemp => 20, :vtotal => 1, :avg => 0 },
  { :stemp => 21, :vtotal => 1, :avg => 0 },
  { :stemp => 22, :vtotal => 2, :avg => 0 },
  { :stemp => 23, :vtotal => 1, :avg => 0 },
  { :stemp => 20, :vtotal => 1, :avg => 0 },
  { :stemp => 21, :vtotal => 1, :avg => 0 },
  { :stemp => 22, :vtotal => 1, :avg => 1 },
  { :stemp => 23, :vtotal => 1, :avg => 0 }
]

First, group your hashes by the stemp.

data = data.group_by { |datum| datum[:stemp] }

Then iterate over each stemp and its entries.

data = data.map do |stemp, entries|
  # this pulls out each hash's :vtotal entry and then combines it with the + operator
  vtotal = entries.map { |entry| entry[:vtotal] }.inject(&:+)
  # this does the same as above, but for the avg entry
  avg = entries.map { |entry| entry[:avg] }.inject(&:+)
  { :stemp => stemp, :vtotal => vtotal, :avg => avg }
end

This outputs

[
  { :stemp => 20, :vtotal => 2, :avg => 0 },
  { :stemp => 21, :vtotal => 2, :avg => 0 },
  { :stemp => 22, :vtotal => 3, :avg => 1 },
  { :stemp => 23, :vtotal => 2, :avg => 0 }
]

Comments

0

This solution suffers from readability but I wanted to provide it for reference.

Hash#merge accepts a block that will be executed when a colliding key is found.

arr = [ {:id => 20, :total => 1, :total2 => 0} ... ]

arr.group_by{ |h| h[:id] }.map do |_, hash|
  hash.reduce do |hash_a, hash_b|
    hash_a.merge(hash_b){ |key, old, new| key == :id ? old : old + new }
  end
end

Comments

0

This one also works

arr.group_by{|t| t[:stemp]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :stemp ? v1 : v1+v2  }}}

change to :id

arr.group_by{|t| t[:id]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :id ? v1 : v1+v2  }}}

Comments

0
[{"4"=>"20.0"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0", "5"=>"10.0"}, {"4"=>"10.0", "5"=>"0.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0", "5"=>"10.66"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0"}].map{|m| m.map{|k,v| v.to_f}.sum()}.sum()

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.