我有两个结构相同的哈希......
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 :(得分: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步。ikeys
和okeys
也可以替换掉,以使其成为单行,但我会不提倡。
第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]]