Ruby中的哈希值减法

5

我有两个哈希表(稍后将转换为数组,不确定是否相关):

Hash1 = {"X"=>2, "Y"=>1, "Z"=>1}

Hash2 = {"X"=>1, "Y"=>1}

我需要像这样进行减法运算(Hash3 = Hash1 - Hash2),并且我需要在这种情况下得到的Hash3的结果是:
Hash3 = {"X"=>1, "Y"=>0, "Z"=>1}

我见过的所有例子和答案都导致值为0(Y)的键在结果哈希中缺失,这不是我需要的。

我正在使用Ruby 2.3.3和Rails 5.0。


1
请详细说明您的示例:不太清楚。为什么选择{"X" => 1}而不是{"X" => 2}{"Y" => 0}从哪里来? - jvillian
刚刚编辑过,语法已经修正。 - Lucas Oliveira
哈希表的值表示数量,因此我有第一个哈希表是我的“库存”,第二个哈希表是我的“当前使用中”,我需要得到的结果哈希表是“可用的”。减法仅适用于值。 - Lucas Oliveira
3个回答

16

您可以合并它们:

h1 = {"X"=>2, "Y"=>1, "Z"=>1}
h2 = {"X"=>1, "Y"=>1}

h1.merge(h2) { |k, v1, v2| v1 - v2 }
#=> {"X"=>1, "Y"=>0, "Z"=>1}

每当一个键同时存在于两个哈希表中时,都会调用该块来确定新值。


由于这种行为,如果一个键只存在于h2中,它将不会导致负值:

h1 = {"X"=>2, "Y"=>1}
h2 = {"X"=>1, "Y"=>1, "Z"=>1}

h1.merge(h2) { |k, v1, v2| v1 - v2 }
#=> {"X"=>1, "Y"=>0, "Z"=>1}

你可能会期望:

#=> {"X"=>1, "Y"=>0, "Z"=>-1}

这就是tadman的解决方案会返回的内容。


1
这是merge的一个非常好的特性。我不知道它可以这样做。 - tadman
2
+1,这个“减去两个哈希值”的案例实际上是文档的用例:https://docs.ruby-lang.org/en/2.0.0/Hash.html#method-i-merge - Mickey Sheu
@MickeySheu 哦,确实 :-) - Stefan
2
太好了。这是最简单的方法,幸运的是我的模型不允许第二个哈希值大于第一个哈希值。谢谢。 - Lucas Oliveira
1
如果 h1 = { "X"=>2, "Y"=>1, "Z"=>1 }h2 = { "X"=>1, "Y"=>1, "W"=>2 }, 并且期望的返回值是 { "X"=>1, "Y"=>0, "Z"=>1 },可以使用以下代码:h1.merge(h2.select { |k,_| h1.key?(k) }) { |k, v1, v2| v1 - v2 } #=> {"X"=>2, "Y"=>0, "Z"=>1} - Cary Swoveland

4

如果你将其分为两个步骤,那么这并不太困难:

def hash_sub(a, b)
  (a.keys + b.keys).uniq.map do |k|
    [ k, a[k].to_i - b[k].to_i ]
  end.to_h
end

hash_sub({"X"=>2, "Y"=>1, "Z"=>1}, {"X"=>1, "Y"=>1})
# => {"X"=>1, "Y"=>0, "Z"=>1}

第一步是通过组合两个哈希表中的键来计算所有可能的(唯一的)键,然后通过从一个哈希表的值减去另一个哈希表的值并使用 .to_i 强制转换为整数来生成新的哈希表。

3
(a.keys + b.keys).uniq 可以简写为 (a.keys | b.keys) - Stefan
2
@Stefan 那是真的。我很想选择那个,但它似乎更加“神秘”。 - tadman
@tadman,将|视为“集合并集”(如“A|B:= A或B中存在的元素”)是很常见的。了解这一点后,对关键集进行|操作就很有意义。 :) - D-side
1
@tadman 我觉得这是Rdoc的问题。它会将所有符号方法聚集在顶部,但却把|放在底部。 - Stefan

0

你也可以将它们映射起来(但合并解决方案更好,因为它会处理从2获取值的情况,即使键在1中不存在):

hash3 = hash1.map{|k,v| { k: (v-(hash2[k.to_s] || 0)) }.stringify_keys }

这将产生一个数组


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接