Принцип работы Hash#merge
согласован: если ключ существует в обоих, то значение ключа объединенного ha sh заменяет первое во всех случаях. Вам понадобится рекурсивное слияние.
В Rails есть deep_merge
. Но если вы не используете Rails или если он не соответствует вашим требованиям, вы можете довольно легко свернуть свой собственный.
Hash#merge
поддерживает блок, который может помочь:
h1.merge(h2) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
old.merge(new) : new }
Это будет работать, если у вас есть только один слой встроенных хешей. Если у вас есть произвольная глубина вложенных хэшей, вы можете патч обезьяны Hash
с deep_merge
:
class Hash
def deep_merge(h)
self.merge(h) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
old.deep_merge(new) : new }
end
end
Или что-то в этом роде ... :) Это будет рекурсивно объединяться, если есть ha sh в обоих случаях, в противном случае заменяет как обычно. Вы можете изменить его на свой вкус.
Примерка для вашего случая:
2.6.1 :008 > class Hash
2.6.1 :009?> def deep_merge(h)
2.6.1 :010?> self.merge(h) { |k, old, new| (old.instance_of?(Hash) && new.instance_of?(Hash)) ?
2.6.1 :011 > old.deep_merge(new) : new }
2.6.1 :012?> end
2.6.1 :013?> end
=> :deep_merge
2.6.1 :014 > h1 = {"a"=>{"x"=>"1","y"=>"2"}, "b"=>"z"}
=> {"a"=>{"x"=>"1", "y"=>"2"}, "b"=>"z"}
2.6.1 :015 > h2 = {"a"=>{"x"=>"3"}, "b"=>"q"}
=> {"a"=>{"x"=>"3"}, "b"=>"q"}
2.6.1 :016 > h1.deep_merge(h2)
=> {"a"=>{"x"=>"3", "y"=>"2"}, "b"=>"q"}
2.6.1 :017 >