You can improve Hash::each
that it will allow you to easily perform your task:
module HashRecursive
refine Hash do
def each(recursive=false, &block)
if recursive
Enumerator.new do |yielder|
self.map do |key, value|
value.each(recursive=true).map{ |key_next, value_next|
yielder << [[key, key_next].flatten, value_next]
} if value.is_a?(Hash)
yielder << [[key], value]
end
end.entries.each(&block)
else
super(&block)
end
end
alias_method(:each_pair, :each)
end
end
using HashRecursive
Here goes the solution for your task:
def sum_hashes(hash) # This method will do the job
result_hash = {}
hash.each(recursive=true) do |keys, value|
if keys.size == 2
result_hash.merge!(value) do |key, value1, value2|
[value1, value2].all? {|v| v.is_a?(Integer)} ? value1+value2 : value2
end
end
end
result_hash
end
# Here is your question's example
hash_1 = {
41 => {"a" => {:a => 4, :b => 8, :c => 3}},
56 => {"b" => {:a => 5, :b => 4, :c => 8}},
57 => {"c" => {:a => 8, :b => 9, :c => 3}}
}
puts sum_hashes(hash_1) # {:a=>17, :b=>21, :c=>14}
# This will work for anything that has similar pattern
hash_2 = {
:a => { "p" => {:a => 1, :b => 2, :c => 0, :d => 5 }},
3 => { "b" => {:a => 2, :b => 2, :c => 100, :d => 0 }},
404 => { "c" => {:a => 3, :b => 2, :c => -100, :d => 15 }},
'24' => { "2" => {:a => 4, :b => 2, :c => 300, :d => 25 }},
11 => { :h => {:a => 5, :b => 2, :c => -350, :d => 40 }},
:x => { "c" => {:a => 6, :b => 2, :c => 50, :d => 5 }}
}
puts sum_hashes(hash_2) # {:a=>21, :b=>12, :c=>0, :d=>90}
Take a look at this answer for more details.