如何将旧散列与新散列合并,当键不在新散列中时,该值应为null

时间:2014-12-01 09:00:39

标签: ruby

假设我从对象获得了旧属性的哈希值:

old = {a: 1, b: 2, c: nil}

然后我从用户输入获得了一个新属性的哈希值(所有键必须存在于旧哈希中):

new = {a: 2}

最后,我希望得到一个属性的哈希值来更新到这个对象:

upd = {a: 2, b: nil, c: nil} 
# upd = {a: 2, b: nil} would be better

现在我使用map来实现这个目标:

upd = Hash[old.map{|x| [ x[0],new[x[0]] ] } ]

我试过了merge,却无法得到我想要的东西:

old.merge(new){|k,old,new| new} #=> {:a=>2, :b=>2, :c=>nil}

但我认为必须有更好的方法来做到这一点。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

这是一种方式:

old.merge(old) { |k,_,_| new[k] }
 #=> {a: 2, b: nil, c: nil}

这使用了方法Hash#merge的形式。该块的目的是解析合并中涉及的两个哈希中存在的键的值。

这里我将old与自身合并,因此old中每个键的值由块确定。

将数组[key, old_value, new_value]传递给块。在这种情况下,old_valuenew_value是相同的,但由于我不会在块中使用该值,因此我已使用占位符_替换了两个相应的块变量。

在块中,我将密钥k的值计算为new[k],如果nil没有密钥new,则为k。< / p>

此方法也可以使用Enumerable#reduce(a.k.a inject)实施:

old.reduce(old) { |h,(k,_)| h[k] = new[k]; h }

答案 1 :(得分:2)

我认为没有一种开箱即用的方式来做你想做的事情,所以你现在的解决方案似乎没问题。

您可以通过以下方式避免[0]

upd = Hash[old.map{|k, v| [k, new[k]] }]

upd = Hash[old.keys.map{|k| [k, new[k]] }]

当前解决方案有一点需要注意:如果new中存在密钥,但old中不存在密钥,则upd中不存在密钥。您可能已经意识到这一点,但我认为应该指出。

答案 2 :(得分:1)

old.inject(new.dup) { |h, (k,v)| h[k] = nil unless (v.nil? || new.has_key?(k)); h }
# => {a: 2, b: nil}

基本上,它说的是new的副本。然后通过迭代h中的(k,v)对来构建生成的哈希old

除了new中的内容之外,我们想要的唯一额外内容是old中的密钥具有非零值,但现在密钥不存在于newnil我们希望通过在结果中包含键来指示,但值为(k,v)。因此,如果oldh相同,h[k] = nil是我们正在构建的结果,那么我们想要添加v k不是,但new甚至不是unless中的关键字。由于那里有一堆否定,用{{1}}表达它更简单,从而产生上述形式。