Redução de hash recursiva, mesclando hashes enquanto soma seus valores.

Recentemente, eu precisava de uma solução rápida para mesclar Hashes com esquemas diferentes, enquanto reduzia seus valores. No meu caso, tive uma grande matriz com muitos relatórios – em hashes – e precisei reduzir todos os seus valores em um relatório mesclado, mais ou menos assim:

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 } } } }

Como não consegui encontrar nada útil que resolvesse isso, escrevi o seguinte HashReducer para me ajudar (veja a essência ):

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

O uso é simples, você precisa de um hash de base e um hash que deseja adicionar a ele:

base = {a: 1.99}
other
= {a: 1, b: 2.99}
HashReducer.reduce!(base: base, add: other)
# {a: 2.99, b: 2.99}

Se você quiser fantasiar, pode transformar o resultado da soma passando um bloco:

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

Se você souber de algo melhor, por favor me avise!