Reason #98 •
April 8th, 2026
ActiveSupport: Hash#deep_merge
Regular Hash#merge is perfect until the values themselves are hashes. At that point, we either start writing recursive merge code by hand, or we use ActiveSupport's deep_merge and get on with our lives.
Ruby
defaults = {
http: {
open_timeout: 5,
headers: {
"Accept" => "application/json",
"User-Agent" => "Loving Ruby"
}
}
}
overrides = {
http: {
headers: {
"User-Agent" => "James Bond"
}
}
}
defaults.deep_merge(overrides)
# => {
# http: {
# open_timeout: 5,
# headers: {
# "Accept" => "application/json",
# "User-Agent" => "James Bond"
# }
# }
# }
JavaScript
const deepMerge = (left, right) => {
const result = { ...left };
for (const [key, value] of Object.entries(right)) {
if (
value &&
typeof value === "object" &&
!Array.isArray(value) &&
result[key] &&
typeof result[key] === "object" &&
!Array.isArray(result[key])
) {
result[key] = deepMerge(result[key], value);
} else {
result[key] = value;
}
}
return result;
};
const defaults = {
http: {
open_timeout: 5,
headers: {
"Accept": "application/json",
"User-Agent": "Loving Ruby"
}
}
};
const overrides = {
http: {
headers: {
"User-Agent": "James Bond"
}
}
};
deepMerge(defaults, overrides);
// => {
// http: {
// open_timeout: 5,
// headers: {
// "Accept": "application/json",
// "User-Agent": "James Bond"
// }
// }
// }
Just what the doctor ordered!
History
Hash#deep_merge landed in Rails 2.2.0, released in 2008.