在ruby中减去两个相同哈希值

时间:2015-03-13 10:37:25

标签: ruby hash

我有两个结构相同的哈希......

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

可能不建议使用此类操作。我也很欣赏这些反馈。 : - )

4 个答案:

答案 0 :(得分:9)

我会这样做:

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

答案 1 :(得分: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}}}

答案 2 :(得分: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}}

答案 3 :(得分: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]]