Hash#merge
hash.merge(*others) -> hash The merge method combines a hash with one or more other hashes, returning a brand new hash. The original hash stays untouched. When the same key appears in multiple hashes, the value from the hash furthest to the right wins. You can also pass a block to control exactly what happens during conflicts.
Signature
hash.merge(*others) -> new_hash
hash.merge(*others) { |key, old_value, new_value| block } -> new_hash
Parameters
| Parameter | Type | Description |
|---|---|---|
*others | Hash | One or more hashes to merge into the receiver. Each argument must be a Hash. |
Return Value
Returns a new Hash containing all key-value pairs from self and every argument hash. The original hash is never modified. If no arguments are given, merge returns a shallow copy of the hash.
Basic Usage
defaults = { theme: "dark", locale: "en", debug: false }
user_settings = { theme: "light", debug: true }
config = defaults.merge(user_settings)
config # => {:theme=>"light", :locale=>"en", :debug=>true}
defaults # => {:theme=>"dark", :locale=>"en", :debug=>false} # unchanged
The original hashes are left alone, so you can reuse them.
Merging Multiple Hashes
You can pass more than one hash. They are merged left to right, meaning each subsequent hash overwrites values from the previous ones.
base = { a: 1 }
h1 = { b: 2, a: 10 }
h2 = { c: 3, a: 100 }
base.merge(h1, h2)
# => {:a=>100, :b=>2, :c=>3}
Block Form (Conflict Resolution)
When duplicate keys exist, you can give a block to decide the outcome yourself. The block receives three arguments: the conflicting key, the value from self, and the value from the hash being merged in.
prices = { apple: 1.5, banana: 0.75, cherry: 2.0 }
discounts = { apple: 0.25, banana: 0.10 }
final = prices.merge(discounts) { |key, price, discount| price - discount }
final # => {:apple=>1.25, :banana=>0.65, :cherry=>2.0}
The block only runs for keys that appear in both hashes. If a key is unique to one hash, the block does not fire for it.
Common conflict-resolution patterns:
h1 = { a: 1, b: 2 }
h2 = { a: 10, b: 20 }
# Keep the higher value
h1.merge(h2) { |_k, v1, v2| v1 > v2 ? v1 : v2 }
# => {:a=>10, :b=>20}
# Add numeric values together
h1.merge(h2) { |_k, v1, v2| v1 + v2 }
# => {:a=>11, :b=>22}
# Collect into an array
h1.merge(h2) { |_k, v1, v2| [v1, v2] }
# => {:a=>[1, 10], :b=>[2, 20]}
merge vs merge! vs update
Ruby gives you three ways to merge:
| Method | Effect | Returns |
|---|---|---|
merge | Creates a new hash | New hash |
merge! | Modifies self in place | self |
update | Alias for merge! | self |
merge! and update are identical — they share the same implementation under the hood. Use whichever name reads better in context.
config = { host: "localhost", port: 3000 }
# Non-destructive: original unchanged
new_config = config.merge(port: 8080)
config # => {:host=>"localhost", :port=>3000}
new_config # => {:host=>"localhost", :port=>8080}
# Destructive: original modified
config.merge!(port: 8080)
config # => {:host=>"localhost", :port=>8080}
Use merge when you want to preserve data. Use merge! or update when you intentionally want to mutate the original.
No Arguments Form
Calling merge with no arguments returns a shallow copy of the hash:
original = { a: 1, b: 2 }
copy = original.merge
copy # => {:a=>1, :b=>2}
copy.object_id == original.object_id # => false
This is equivalent to original.dup, but reads more clearly in context.
Common Mistakes
Nested hashes are not deep-merged. A one-level shallow merge is all you get. The nested hash is replaced entirely:
h = { a: { b: 1 } }
h.merge(a: { b: 2 })
# => {:a=>{:b=>2}} -- the inner hash is replaced, not merged
Without a block, later values win silently. Ruby does not warn you about overwrites. If you need to detect conflicts, use the block form.
The block is ignored when there are no duplicate keys. This is fine in most cases, but can cause confusion if your block has side effects that you expect to run.
See Also
Hash#fetch— Retrieve a value with a default or blockHash#select— Keep only selected key-value pairsHash#reject— Remove entries matching a condition