【问题标题】:Subtracting Values in two identical hashes in ruby在 ruby​​ 中减去两个相同哈希中的值
【发布时间】:2015-03-14 06:01:39
【问题描述】:

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

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

也许不推荐这种类型的操作。我也很感激你的反馈。 :-)

【问题讨论】:

标签: ruby hash


【解决方案1】:

我会这样做:

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

【讨论】:

  • 非常好。读者:这使用Hash#merge 的形式,它使用一个块来确定合并的两个哈希中存在的键的值
  • 我把代码改成了hash1.merge(hash2) { |_, oldval, newval| oldval.merge(newval) { |_, x, y| x - y } },这样我就可以用byebug来调试了。还有,谢谢!
【解决方案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}}}

【讨论】:

  • 不错。此解决方案比 spickerman 的解决方案更通用,因为它适用于任何嵌套深度。唯一的限制是两个哈希必须具有相同的结构。
  • @undur_gongor,我举了一个嵌套哈希的例子。
【解决方案3】:
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}}

【讨论】:

    【解决方案4】:

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

    第一步:提取内键和外键:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 2013-03-29
      • 1970-01-01
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多