Skip to content

Instantly share code, notes, and snippets.

@atheiman
Last active July 17, 2016 18:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atheiman/24ba3daa9d2fb4dcc861476e8e99cb48 to your computer and use it in GitHub Desktop.
Save atheiman/24ba3daa9d2fb4dcc861476e8e99cb48 to your computer and use it in GitHub Desktop.
Deep merge hashes. If both values are numeric they are summed. (Ruby)

Deep merge hashes. If both values are numeric they are summed.

$ ruby deep_sum_merge.rb
source:
layer_1: 10
deeper:
  layer_2: 20
  deeper:
    layer_3: 30
    deeper:
      some_text: im gonna get overwritten
      deeper:
        layer_5: 50

destination:
layer_1: 100
deeper:
  deeper:
    layer_3: 300
    deeper:
      layer_4: 400
      some_text: non-numerics are replaced as expected with Hash.merge
      deeper:
        layer_5: 500

merged result with sums:
layer_1: 110
deeper:
  layer_2: 20
  deeper:
    layer_3: 330
    deeper:
      some_text: non-numerics are replaced as expected with Hash.merge
      deeper:
        layer_5: 550
      layer_4: 400

It worked :)

The actual merge logic you are looking for here in the code below

require 'yaml'
source = {
'layer_1' => 10,
'deeper' => {
'layer_2' => 20,
'deeper' => {
'layer_3' => 30,
'deeper' => {
'some_text' => 'im gonna get overwritten',
'deeper' => {
'layer_5' => 50
}
}
}
}
}
destination = {
'layer_1' => 100,
'deeper' => {
'deeper' => {
'layer_3' => 300,
'deeper' => {
'layer_4' => 400,
'some_text' => 'non-numerics are replaced as expected with Hash.merge',
'deeper' => {
'layer_5' => 500
}
}
}
}
}
expected = {
'layer_1' => 110,
'deeper' => {
'layer_2' => 20,
'deeper' => {
'layer_3' => 330,
'deeper' => {
'layer_4' => 400,
'some_text' => 'non-numerics are replaced as expected with Hash.merge',
'deeper' => {
'layer_5' => 550
}
}
}
}
}
puts 'source:'
puts source.to_yaml.gsub(/^---\n/, '')
puts
puts 'destination:'
puts destination.to_yaml.gsub(/^---\n/, '')
puts
# store the merge logic
# http://ruby-doc.org/core-2.2.3/Hash.html#method-i-merge
def sum_merge(_, oldval, newval)
if oldval.is_a?(Hash) && newval.is_a?(Hash)
newval.merge!(oldval) { |_, oldval, newval| sum_merge(_, oldval, newval) }
elsif oldval.is_a?(Numeric) && newval.is_a?(Numeric)
oldval + newval
else
newval
end
end
# use it
destination.merge!(source) { |_, oldval, newval| sum_merge(_, oldval, newval) }
puts 'merged result with sums:'
puts destination.to_yaml.gsub(/^---\n/, '')
puts
puts destination == expected ? 'It worked :)' : 'It did not work :('
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment