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
- Hash#merge — the non-mutating counterpart that returns a new Hash
- Hash#dig — retrieve nested values from a hash
- Hash#transform_values — transform all values in a hash in place