l_03qg
Last Updated: February 25, 2016
·
332
· lucasmartins

# Recursive Hash Reduce, merging hashes while summing its values.

###### aggregation

Recently I needed a quick solution for merging Hashes with different schemes, while reducing its values. In my case, I've had a big array with a lot of reports - in hashes - and I needed to reduce all its values into a merged report, pretty much like this:

``````hash_a = { { a: 1, b: 2, c: { d: 3, e: { f: 4, g: 5 } } } }
hash_b = { { a: 1, b: 2, c: { d: 3 } } }
reduced_hash = { { a: 2, b: 4, c: { d: 6, e: { f: 4, g: 5 } } } }``````

As I couldn't find anything handy that solved this, I've wrote the following HashReducer to help me out (see the gist):

``````module HashReducer
# use the block to transform the addition/reduce result for each value
def self.reduce!(base: nil, add: nil, &block)
base = {} unless base
fail 'base: must be a hash!' unless base.is_a?(Hash)
if v.is_a?(Hash)
base[k] = {} unless base[k]
base[k] = self.reduce!(base: base[k], add: v, &block)
else
if block_given?
base[k] = block.call(base[k])
end
end
end
base
end

private

unless base[k]
base[k] = 0
else
fail "Don't know how to initialize a #{add[k].class} object!"
end
end
end
end``````

Usage is simple, you need a base hash, and a hash you want to add to it:

``````base = {a: 1.99}
other = {a: 1, b: 2.99}
# {a: 2.99, b: 2.99}``````

If you wanna get fancy, you can transform the sum result by passing a block:

``````HashReducer.reduce!(base: base, add: other) do |sum|
sum.round
end
# {a: 3, b: 3}``````

If you know something better, please let me know!