rubyguides

Hash#merge!

merge!(*other_hashes) { |key, old_value, new_value| block_result } → self

Signature

merge!(*other_hashes) → self
merge!(*other_hashes) { |key, old_value, new_value| block_result } → self

Hash#merge! accepts one or more Hash arguments and merges them into self. It always returns self — the same object that was modified, not a copy.

The block is optional. When provided, it is called only for keys that exist in both hashes.


Basic Usage

config = { environment: "development", port: 3000 }
overrides = { port: 8080, debug: true }

config.merge!(overrides)
config
# => { environment: "development", port: 8080, debug: true }

The values from overrides take precedence over config for duplicate keys. port was 3000 and becomes 8080. debug was absent and is added.


Resolving Conflicts With a Block

When a key appears in both hashes and you pass a block, the block decides the final value:

scores = { alice: 10, bob: 20 }
bonus = { alice: 5, bob: 5 }

scores.merge!(bonus) { |_key, old, new| old + new }
scores
# => { alice: 15, bob: 25 }

The block receives three arguments: key, old_value (from self), and new_value (from the hash being merged in). Its return value becomes the stored value.

Without a block, the new value silently wins:

h1 = { a: 1, b: 2 }
h2 = { b: 3 }

h1.merge!(h2)
h1
# => { a: 1, b: 3 }

No Arguments

Calling merge! with no arguments returns self unchanged:

h = { a: 1, b: 2 }
h.merge!
# => { a: 1, b: 2 }

If you pass a block with no arguments, the block is silently ignored.


Multiple Hashes

You can merge more than one hash at once. They are processed left to right:

defaults = { timeout: 30, retries: 3 }
env_config = { timeout: 60 }
user_config = { retries: 5 }

defaults.merge!(env_config, user_config)
defaults
# => { timeout: 60, retries: 5 }

Each subsequent hash overwrites keys set by the previous ones.


The update Alias

Hash#update is the same method as Hash#merge!:

h = { a: 1 }
h.update(b: 2)
h
# => { a: 1, b: 2 }

Use whichever name reads better in context.


Gotchas

Mutation risk. merge! modifies self in place. If the same Hash object is referenced by another variable, you will see those changes reflected there too:

original = { a: 1 }
copy = original
original.merge!(b: 2)

original
# => { a: 1, b: 2 }
copy
# => { a: 1, b: 2 }  # also changed

If you need a non-mutating version, see Hash#merge.

TypeError on non-Hash arguments. Passing a non-Hash object raises a TypeError:

h = { a: 1 }
h.merge!("not a hash")
# => TypeError: no implicit conversion of String into Hash

See Also