Deep Merge Two Nested Hashes Without Losing Keys In Ruby
Perform a recursive hash merge that combines nested values instead of overwriting them — useful for configuration merging, settings overrides, and building complex data structures.
Description
Ruby’s built-in Hash#merge is shallow — it overwrites nested hashes entirely when keys conflict. A deep merge recursively combines nested hashes so both sides contribute keys at every level.
The pattern: if both values for a key are Hash, recurse into them. Otherwise use the right-hand value (override behavior). Implement as a recursive method or a merge block.
Rails provides Hash#deep_merge in ActiveSupport. For plain Ruby, implement it with a recursive block or extend Hash.
Sample input:
defaults = { db: { host: "localhost", port: 5432 }, cache: { ttl: 60 } }
overrides = { db: { port: 5433, ssl: true } }
Sample Output:
{ db: { host: "localhost", port: 5433, ssl: true }, cache: { ttl: 60 } }
Answer
defaults = { db: { host: "localhost", port: 5432 }, cache: { ttl: 60 } }
overrides = { db: { port: 5433, ssl: true } }
# Using merge with a recursive block
def deep_merge(base, overrides)
base.merge(overrides) do |_key, base_val, override_val|
if base_val.is_a?(Hash) && override_val.is_a?(Hash)
deep_merge(base_val, override_val)
else
override_val
end
end
end
deep_merge(defaults, overrides)
# => { db: { host: "localhost", port: 5433, ssl: true }, cache: { ttl: 60 } }
# In-place variant
def deep_merge!(base, overrides)
base.merge!(overrides) do |_key, base_val, override_val|
base_val.is_a?(Hash) && override_val.is_a?(Hash) ? deep_merge!(base_val, override_val) : override_val
end
end
# Rails / ActiveSupport (if available)
require 'active_support/core_ext/hash/deep_merge'
defaults.deep_merge(overrides)
# => { db: { host: "localhost", port: 5433, ssl: true }, cache: { ttl: 60 } }
Check viewARU - Brand Newsletter!
Newsletter to DEVs by DEVs - boost your Personal Brand & career! 🚀