/ Tags: RUBY-CODE / Categories: SOLUTIONS

Deep Merge Nested Hashes In Ruby Without Losing Keys

Ruby’s built-in Hash#merge only merges the top level — nested keys get overwritten entirely instead of merged recursively. When working with deeply nested configuration objects or API payloads, you need a merge that walks the full structure.

Description

Hash#merge does a shallow merge: if both hashes have the same key at the top level and its value is itself a hash, the source value replaces the destination value entirely. deep_merge recursively merges nested hashes, preserving keys from both sides and only overwriting at the leaf level where values genuinely conflict. Rails ships deep_merge as part of ActiveSupport. In plain Ruby, you implement it with a simple recursive approach using merge with a block. This pattern is essential for merging configuration files, layering environment-specific overrides onto defaults, or combining API response objects.

Sample input:

  defaults = {
    database: { pool: 5, timeout: 30 },
    cache: { ttl: 300 }
  }

  overrides = {
    database: { pool: 10 },
    logging: { level: "info" }
  }


Sample Output:

  {
    database: { pool: 10, timeout: 30 },
    cache: { ttl: 300 },
    logging: { level: "info" }
  }

Answer

  def deep_merge(base, overrides)
    base.merge(overrides) do |_key, old_val, new_val|
      if old_val.is_a?(Hash) && new_val.is_a?(Hash)
        deep_merge(old_val, new_val)
      else
        new_val
      end
    end
  end

  # In Rails (ActiveSupport):
  # defaults.deep_merge(overrides)

Learn More

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! 💻 🏡 Grad. Student, MCS. 🎓 Class of '23. GitKraken Ambassador 🇳🇵 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More