Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge Ruby nested hashes with same keys

I have several hashes in Ruby which have nested hashes inside of them an share very similar structure. They look something like this:

a = {
    "year_1": {
        "sub_type_a": {
            "label1": value1
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2
        }
    }
}

b = {
    "year_1": {
        "sub_type_a": {
            "label3": value3
        }
    },
    "year_2": {
        "sub_type_a": {
            "label4": value4
        }
    }
}

c = {
    "year_1": {
        "sub_type_a": {
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label6": value6
        }
    }
}

I want to combine them in one single hash which would have the nested data combined where possible without overwriting other values like this:

result = {
    "year_1": {
        "sub_type_a": {
            "label1": value1,
            "label3": value3,
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2,
            "label4": value4,
            "label6": value6
        }
    }
}

There could also be several sub types instead of just one but that's the general idea.

If I use the merge function it just overwrites the label-value data inside the sub_type hashes and I am left with only one record.

Is there a simple way to achieve this? I can write a function that iterates the hashes recursively and figure out inside what to add where but it feels like that there should be a simpler way.

like image 673
mmvsbg Avatar asked Apr 30 '26 04:04

mmvsbg


2 Answers

Something similar.

Combine each_with_object, each and merge so you can iterate trough each hash and assign the merged values when they exist to a temporal new one:

[a, b, c].each_with_object({}) do |years_data, hash|
  years_data.each do |year, data|
    hash[year] = (hash[year] || {}).merge(data) { |_, oldval, newval| oldval.merge(newval) }
  end
end
# {
#     :year_1 => {
#         :sub_type_a => {
#             :label1 => :value1,
#             :label3 => :value3,
#             :label5 => :value5
#         }
#     },
#     :year_2 => {
#         :sub_type_a => {
#             :label2 => :value2,
#             :label4 => :value4,
#             :label6 => :value6
#         }
#     }
# }
like image 110
Sebastian Palma Avatar answered May 01 '26 18:05

Sebastian Palma


If you are using Rails (or ActiveSupport) you might want to look at deep_merge, which handles merging of nested hashes for you

like image 33
johansenja Avatar answered May 01 '26 16:05

johansenja