How to merge two hashes with no new keys

坚强是说给别人听的谎言 提交于 2019-11-28 21:29:07

You could remove keys that weren't in the first hash from the second hash, then merge:

h.merge j.select { |k| h.keys.include? k }

Unlike my edited-out alternative, this is safe if you decide to change it to a merge! or update.

Yjerem's answer works in Ruby 1.9, but not in 1.8.x. In 1.8.x the Hash#select method returns an array. Hash#reject returns a hash.

h.reject { |k,v| !j.keys.include? k }

If you want to keep only key-value pairs that have identical values, you can do this:

h.reject { |k,v| j[k] != h[k] }

The edge case there is nils. If you are storing nils in your Hash then you have to do this:

h.reject { |k,v| !j.has_key? k or j[k] != h[k] }

If you're using activesupport (part of rails), you can take advantage of 2 extra methods on Hash:

  • Hash#slice takes the desired keys as separate arguments (not an array of keys) and returns a new hash with just the keys you asked for.
  • Hash#except takes the same arguments as slice, but returns a new hash with keys that were not in the arguments.

First load activesupport:

require 'active_support/core_ext'

Merge only entries from j whose keys are already in h (i.e. modify, but don't add any or remove entries in h):

h.merge(j.slice(*h.keys))

Example:

ignore_new = ->(h, j) { h.merge(j.slice(* h.keys)) }
ignore_new.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:a=>1, :b=>10, :c=>11}

Get the leftovers from j that weren't in h:

j.except(*h.keys)

Bonus:

If you want true intersection, meaning you want a result that only has keys that are in common between the 2 hashes, do this:

h.merge(j).slice(* ( h.keys & j.keys ) )

Example:

intersect = ->(h, j) { h.merge(j).slice(* (h.keys & j.keys) ) }
intersect.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:b=>10, :c=>11}

and leftovers from h that weren't in j:

h.except(*j.keys)

You may also want to use activesupport's HashWithIndifferentAccess if you want string & symbol key-access to be considered equivalent.

Note that none of the above examples change the original hashes; new hashes are returned instead.

[h].inject({}) { |m,e| e.merge(j) { |k,o,n| m[k] = n }; m}

or

[{}].inject(h) { |m,e| m.merge(j) { |k,o,n| e[k] = n }; e}

or (probably the best, but not technically a single expression) ...

t = {}; h.merge(j) { |k,o,n| t[k] = n }; t

The more customized way of doing this is:

 h = {"foo"=> "bar"}

 j = {"foo" => "baz", "extra" => "value"}


 k = h.merge(j)
result:  {"foo"=>"baz", "extra"=>"value"}

Here the key "foo" in the second hash is overriding the "foo" in first hash.But if you want to keep the old value i.e bar or if you want keep the new value i.e "baz"? You can do something like this:

  k = h.merge(j){|key, old, new| old}
 result: {"foo"=>"bar", "extra"=>"value"}


k = h.merge(j){|key, old, new| new}

result: {"foo"=>"baz", "extra"=>"value"}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!