Hash#merge

hash.merge(*others) -> hash
Returns: hash · Added in v1.8 · Updated March 13, 2026 · Hash Methods
ruby hash methods merge

The merge method combines a hash with one or more other hashes, returning a new hash. When keys overlap between hashes, the value from the later hash wins. This makes merge essential for combining configurations, updating defaults, and building up hashes from multiple sources.

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 must be a Hash object.

Return Value

Returns a new Hash object containing all key-value pairs from the original hash and the merged hashes. The original hashes remain unchanged.

Basic Usage

defaults = { theme: 'dark', language: 'en' }
user_prefs = { theme: 'light' }

merged = defaults.merge(user_prefs)
merged # => {:theme=>"light", :language=>"en"}

The original hashes stay intact:

defaults  # => {:theme=>"dark", :language=>"en"}
user_prefs # => {:theme=>"light"}

You can merge multiple hashes at once:

base = { a: 1 }
overrides = { b: 2 }
local = { c: 3 }

base.merge(overrides, local)
# => {:a=>1, :b=>2, :c=>3}

The last value wins when keys overlap:

h1 = { a: 1, b: 2 }
h2 = { b: 3, c: 4 }
h3 = { b: 5 }

h1.merge(h2, h3)
# => {:a=>1, :b=>5, :c=>4}

In-Place vs New Hash

Ruby provides two ways to merge: one that creates a new hash, and one that modifies in place.

merge (Creates New Hash)

The merge method returns a new hash and leaves the original unchanged:

config = { host: 'localhost', port: 3000 }
new_config = config.merge({ port: 8080 })

config     # => {:host=>"localhost", :port=>3000}
new_config # => {:host=>"localhost", :port=>8080}

This is useful when you want to preserve the original data.

merge! (Modifies in Place)

The merge! method (also known as update) modifies the hash directly:

config = { host: 'localhost', port: 3000 }
config.merge!({ port: 8080 })

config # => {:host=>"localhost", :port=>8080}

Use this when you intentionally want to update the original hash. It’s faster since it doesn’t create a new object.

Block Form (Conflict Resolution)

When keys overlap, you can provide a block to resolve conflicts yourself. The block receives three arguments: the key, the value from the original hash, and the value from the merging hash.

prices = { apple: 1.00, banana: 0.50 }
sale_prices = { apple: 0.80, banana: 0.40, orange: 0.75 }

prices.merge(sale_prices) { |key, old, new| old * 0.9 }
# => {:apple=>0.9, :banana=>0.36, :orange=>0.75}

The block lets you compute custom values. Common patterns include:

# Keep the higher value
h1.merge(h2) { |_k, v1, v2| v1 > v2 ? v1 : v2 }

# Sum numeric values
h1.merge(h2) { |_k, v1, v2| v1 + v2 }

# Collect values into arrays
h1.merge(h2) { |_k, v1, v2| [v1, v2] }

When there’s no conflict, the block is never called:

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

h1.merge(h2) { |key, old, new| raise 'Never called' }
# => {:a=>1, :b=>2}

Common Use Cases

Configuration Defaults

DEFAULTS = { timeout: 30, retries: 3, debug: false }

user_config = { timeout: 60 }

config = DEFAULTS.merge(user_config)
# => {:timeout=>60, :retries=>3, :debug=>false}

Merging User Input

form_data = { name: '', email: '' }
required_fields = { name: nil, email: nil, phone: nil }

submitted = { name: 'Alice', email: 'alice@example.com' }

form_data.merge(required_fields).merge(submitted)
# => {:name=>"Alice", :email=>"alice@example.com", :phone=>nil}

Building Options Hashes

default_options = { 
  verbose: false, 
  format: 'json', 
  timeout: 30 
}

user_options = { verbose: true }

default_options.merge(user_options)
# => {:verbose=>true, :format=>"json", :timeout=>30}

See Also