Recursive Hash Reduce, merging hashes while summing its values.
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
add = {} unless add
fail 'base: must be a hash!' unless base.is_a?(Hash)
fail 'add: must be a hash!' unless add.is_a?(Hash)
add.each do |k,v|
if v.is_a?(Hash)
base[k] = {} unless base[k]
base[k] = self.reduce!(base: base[k], add: v, &block)
else
initialize_value_field!(base, add, k)
base[k] += add[k] if add[k].respond_to?(:+)
if block_given?
base[k] = block.call(base[k])
end
end
end
base
end
private
def self.initialize_value_field!(base, add, k)
unless base[k]
if add[k].respond_to?(:new)
base[k] = add[k].class.new
elsif add[k].respond_to?(:+)
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}
HashReducer.reduce!(base: base, add: other)
# {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!
Written by Lucas Neves Martins
Related protips
Have a fresh tip? Share with Coderwall community!
Post
Post a tip
Best
#Ruby
Authors
Sponsored by #native_company# — Learn More
#native_title#
#native_desc#