如何合并两个哈希没有新的键

我怎么能合并两个哈希导致没有新的密钥,这意味着合并会合并两个哈希中存在的密钥?

例如,我想要以下内容:

h = {:foo => "bar"} j = {:foo => "baz", :extra => "value"} puts h.merge(j) # {:foo => "baz"} 

我正在寻找一种非常干净的方法,因为我当前的实现非常混乱。

您可以从第二个哈希中删除不在第一个哈希中的键,然后合并:

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

与我编辑的替代方案不同,如果您决定将其更改为merge! ,这是安全的merge!update

如果你正在使用activesupport(rails的一部分),你可以在Hash上利用2个额外的方法:

  • Hash#slice将所需的键作为单独的参数(不是键数组),并返回一个新哈希,只包含您要求的键。
  • Hash#except使用与slice相同的参数,但返回带有不在参数中的键的新哈希。

首先加载activesupport:

 require 'active_support/core_ext' 

仅合并其键已经在h j的条目(即修改,但不添加任何或删除h中的条目):

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

例:

 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} 

j中获取不在h的剩余物:

 j.except(*h.keys) 

奖金:

如果你想要真正的交集,意味着你想要一个只有两个哈希之间共同的键的结果,那么执行以下操作:

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

例:

 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} 

和来自h剩余物不在j

 h.except(*j.keys) 

如果您希望字符串和符号键访问被视为等效,您可能还想使用activesupport的HashWithIndifferentAccess

请注意,上述示例均未改变原始哈希值; 而是返回新的哈希值。

Yjerem的答案适用于Ruby 1.9,但不适用于1.8.x. 在1.8.x中, Hash#select方法返回一个数组。 Hash#reject返回哈希值。

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

如果您只想保留具有相同值的键值对,则可以执行以下操作:

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

边缘案例有nils。 如果你在nash中存储nils,那么你必须这样做:

 h.reject { |k,v| !j.has_key? k or j[k] != h[k] } 
 [h].inject({}) { |m,e| e.merge(j) { |k,o,n| m[k] = n }; m} 

要么

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

或者 (可能是最好的,但在技术上不是单一表达式)……

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

更加个性化的方式是:

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

这里第二个哈希中的键“foo”覆盖了第一个哈希中的“foo”。但是如果你想保留旧值,即bar,或者你想保留新值即“baz”? 你可以这样做:

  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"}