rubyguides

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

ParameterTypeDescription
*othersHashOne 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:

MethodEffectReturns
mergeCreates a new hashNew hash
merge!Modifies self in placeself
updateAlias 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 block
  • Hash#select — Keep only selected key-value pairs
  • Hash#reject — Remove entries matching a condition