2015-03-13 53 views
1

我有两个哈希值是在结构...减去两个相同的哈希值在红宝石

hash1 = {:total=>{:gold=>100, :dark=>500}, :defensive=>{:gold=>100, :dark=>500}} 
hash2 = {:total=>{:gold=>20, :dark=>200}, :defensive=>{:gold=>20, :dark=>200}} 

我要减,并返回结果如下......

hash1 - hash2 => {:total=>{:gold=>80, :dark=>300}, :defensive=>{:gold=>80, :dark=>300}} 

也许不建议这种类型的操作。我也很欣赏这种反馈。 :-)

+0

尝试http://stackoverflow.com/questions/11958374/subtract-values-in-hash-from-corresponding-values-in-another-hash – Sach 2015-03-13 10:42:32

回答

8

我只想做:

hash1 = {:total=>{:gold=>100, :dark=>500}, :defensive=>{:gold=>100, :dark=>500}} 
hash2 = {:total=>{:gold=>20, :dark=>200}, :defensive=>{:gold=>20, :dark=>200}} 

hash1.merge(hash2) { |_, l, r| l.merge(r) { |_, x, y| x - y } } 
#=> {:total=>{:gold=>80, :dark=>300}, :defensive=>{:gold=>80, :dark=>300}} 
+0

完美的解决方案:) – user3118220 2015-03-13 12:00:17

+0

非常好。读者:这使用[Hash#merge](http://ruby-doc.org/core-2.2.0/Hash.html#method-i-merge)的形式,它使用一个块来确定键的值都存在于两个哈希被合并 – 2015-03-13 13:43:37

+0

我将代码改为'hash1.merge(hash2){| _,oldval,newval | oldval.merge(newval){| _,x,y | x - y}}'所以我可以使用byebug进行调试。另外,谢谢! – thedanotto 2015-03-13 14:17:16

1
hash1 = {:total=>{:gold=>100, :dark=>500}, 
     :defensive=>{:gold=>100, :dark=>500}} 

hash2 = {:total=>{:gold=>20, :dark=>200}, 
     :defensive=>{:gold=>20, :dark=>200}} 

{}.tap do |hash| 
    hash1.each do |key, subhash1| 
    subhash2 = hash2[key] 
    hash[key] ||= {} 

    subhash1.each do |k, val1| 
     val2 = subhash2[k] 
     hash[key][k] = val1 - val2 
    end 
    end 
end 

输出是:

{:total=>{:gold=>80, :dark=>300}, 
:defensive=>{:gold=>80, :dark=>300}} 
2

你可以使用递归:

def diff(f,g) 
    f.each_with_object({}) do |(k,v),h| 
    h[k] = 
    case v 
    when Fixnum then v-g[k] 
    else diff v,g[k] 
    end 
    end 
end 

diff hash1, hash2 
    #=> {:total=> {:gold=>80, :dark=>300}, 
    # :defensive=>{:gold=>80, :dark=>300}} 

@under_gongor指出,这适用于并行嵌套散列。这里有一个例子:

hash1 = {:total=>{:gold=>350, :dark=>500}, 
        :defensive=>{:next=>{:gold=>300, :dark=>500}, 
           :last=>{:gold=>150, :dark=>300}}} 
hash2 = {:total=>{:gold=>300, :dark=>100}, 
        :defensive=>{:next=>{:gold=>100, :dark=>200}, 
           :last=>{:gold=>100, :dark=>200}}} 
diff hash1, hash2 
    #=> {:total=>{:gold=> 50, :dark=>400}, 
    #    :defensive=>{:next=>{:gold=>200, :dark=>300}, 
    #       :last=>{:gold=> 50, :dark=>100}}} 
+0

不错。这个解决方案比spickerman更普遍,因为它可以处理任何嵌套深度。唯一的限制是两个哈希必须具有相同的结构。 – 2015-03-13 13:06:49

+0

@undur_gongor,我举了一个嵌套散列的例子。 – 2015-03-13 13:37:50

0

这是另一种方法。对于目前的问题,这可能不是优选的,但它说明了一种有时有用的通用技术。

步骤1:提取内部和外部键:

okeys, ikeys = hash1.keys, hash1.values.first.keys 
    #=> [[:total, :defensive], [:gold, :dark]] 

步骤2:提取数值,并计算差异

a = [hash1,hash2]. 
     map { |h| h.values.map { |g| g.values_at(*ikeys) } }. 
     transpose. 
     map(&:transpose). 
     map { |a| a.reduce(:-) } 
    #=> [[100, 20], [100, 20]] 

步骤3:构建输出散列

okeys.zip(a.map { |b| ikeys.zip(b).to_h }).to_h 
    #=> {:total=>{:gold=>100, :dark=>20}, :defensive=>{:gold=>100, :dark=>20}} 

可以通过在步骤3中替换a来合并步骤2和步骤3. ikeysokeys也可以被替换出来,以使其成为一行,但我不会主张这一点。

解释第2步

第2步可能显得有点复杂,但它真的不是,如果你去通过操作一次一个:

删除的数值,使用Hash#values_at保证正确排序:

b = [hash1,hash2].map { |h| h.values.map { |g| g.values_at(*ikeys) } } 
    #=> [[[100, 500], [100, 500]], [[20, 200], [20, 200]]] 

操纵阵列中,直至它是在适当的形式,用于计算差异:

c = b.transpose 
    #=> [[[100, 500], [20, 200]], [[100, 500], [20, 200]]] 
d = c.map(&:transpose) 
    #=> [[[100, 20], [500, 200]], [[100, 20], [500, 200]]] 

计算差异:

a = d.map { |a| a.reduce(:-) } 
    #=> [[100, 20], [100, 20]]