【问题标题】:Subtract values in hash from corresponding values in another hash从另一个哈希中的对应值中减去哈希中的值
【发布时间】:2012-08-14 18:18:53
【问题描述】:

我希望能够在 Ruby 中减去两个哈希并获得第三个哈希。

这两个哈希看起来像这样:

h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0

h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0

我希望能够像这样调用 h1 上的方法:

h1.difference(h2)

并得到这个哈希结果:

{"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}

我想创建一个新的散列,其中的键来自散列,新散列的值是第一个散列中的键值减去第二个散列中该键的值。问题是,无论键的大小写如何,我都希望这个 Hash 方法能够工作。换句话说,我希望“Cat”与“cat”匹配。

这是我目前所拥有的:

class Hash
  def difference(another_hash)
    (keys + another_hash.keys).map { |key| key.strip }.uniq.inject(Hash.new(0)) { |acc, key| acc[key] = (self[key] - another_hash[key]); acc }.delete_if { |key, value| value == 0 }
  end
end

这没关系,但不幸的是,结果不是我想要的。

任何帮助将不胜感激。

【问题讨论】:

  • 在示例中,键名不同的情况示例 Dog in first and dog is second 是错误的还是预期的?
  • 这符合预期。我有哈希,其中一个哈希中的大小写可能全部为小写,而另一种哈希中的大小写混合。我希望能够匹配,例如,“狗”与“狗”,或“狗”与“狗”等。
  • 如果 h1={"cat" => 1, "Cat" => 2} h2={"cAt" => 3} 会发生什么?
  • 对密钥进行预处理以使其标准化是不可能的吗?
  • 我没有遇到过这样的案例,所以我没有想到那个。这是个好问题。我认为一种方法是在结果哈希中没有“cat”(或“Cat”或“cAt”),因为h1中的“cat”和“Cat”的值等于h2中的“cAt”的值。

标签: ruby hash


【解决方案1】:

如何将哈希转换为集合。

require 'set'

h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0

h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0

p (h1.to_set - h2.to_set)
#=> #<Set: {["Cat", 100], ["Dog", 5], ["Bird", 2]}>

【讨论】:

  • 我不喜欢减法的结果与您的预期有何不同。我期待一个哈希,但我得到了一个挫折。否则,这比其他答案要简洁得多。
  • 这不会减去键的 ,这是 OP 要求的。例如,在期望的结果中,"Dog" =&gt; 2.
【解决方案2】:

作为推荐...

我过去用过这样的东西:

class Hash
  def downcase_keys
    Hash[map{ |k,v| [k.downcase, v]}]
  end

  def difference(other)
    Hash[self.to_a - other.to_a]
  end
  alias :- :difference
end

这让我可以做以下事情:

irb(main):206:0> h1.downcase_keys - h2.downcase_keys
{
     "cat" => 100,
     "dog" => 5,
    "bird" => 2
}
irb(main):207:0> h2.downcase_keys - h1.downcase_keys
{
      "cat" => 50,
      "dog" => 3,
     "bird" => 4,
    "mouse" => 75
}

alias 为您提供了使用- 而不是difference 的良好语法,类似于对数组和集合使用-。你仍然可以使用difference

irb(main):210:0> h1.downcase_keys.difference(h2.downcase_keys)
{
     "cat" => 100,
     "dog" => 5,
    "bird" => 2
}
irb(main):211:0> h2.downcase_keys.difference(h1.downcase_keys)
{
      "cat" => 50,
      "dog" => 3,
     "bird" => 4,
    "mouse" => 75
}

我总是规范化我的哈希键,并且不允许变体泄漏。当您不知道键被称为什么时,这会使处理哈希变得非常困难,因此我强烈建议您这样做第一步。这是一个代码维护问题。

否则,您可以创建原始键名及其规范化名称的映射,但如果您的散列包含两个唯一大小写键(例如“key”和“KEY”),则会遇到问题,因为规范化会踩踏一个。

【讨论】:

  • 你重新定义了数组减法吗?内置方法的行为类似于集合减法。
【解决方案3】:

抱歉,由于时间限制(我现在必须照顾我的男婴),才想出这个愚蠢但有效的代码:

h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h1.default = 0
h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
h2.default = 0
h3 = {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}

class Hash
  def difference(subtrahend)
    diff = {}
    self.each_pair do |k1, v1|
      flag = false
      subtrahend.each_pair do |k2, v2|
        if k1.downcase == k2.downcase
          flag = true
          v_diff = v1 - v2
          break if v_diff == 0
          v_diff > 0 ? diff[k1] = v_diff : diff[k2] = v_diff
        end
      end
      diff[k1] = v1 unless flag
    end
    subtrahend.each_pair do |k2, v2|
      flag = false
      self.each_pair do |k1, v1|
        if k1.downcase == k2.downcase
          flag = true
          break
        end
      end
      diff[k2] = -v2 unless flag
    end
    return diff
  end
end

h1.difference(h2) == h3 ? puts("Pass") : puts("Fail") #=> "Pass"

【讨论】:

  • 不客气。我认为您可以接受一个满足您要求的最佳答案。顺便说一句,我刚刚发布了另一个更好的答案。
【解决方案4】:

我收到了这个https://github.com/junegunn/insensitive_hash

然后按照您的程序进行操作,但根据需要稍作调整

require 'insensitive_hash'

h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}.insensitive
h1.default = 0

h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}.insensitive
h2.default = 0


class Hash
  def difference(another_hash)
    (keys + another_hash.keys).map { |key|
      key.downcase }.uniq.inject(Hash.new(0)) do |acc, key|
      val = self[key] - another_hash[key]
      acc[key] = val if val!= 0
      acc
    end
  end
end

h1.difference(h2)
# => {"cat"=>50, "dog"=>2, "bird"=>-2, "mouse"=>-75} 

【讨论】:

    【解决方案5】:

    这次我想提供另一种解决方案:归一化 -> 存储原始键值对 -> 抓取具有较大值的原始键作为差异的键。

    h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
    h1.default = 0
    h2 = {"cat" => 50, "dog" => 3, "BIRD" => 4, "Mouse" => 75, "Snake" => 10}
    h2.default = 0
    h3 = {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
    
    
    class Hash
      def difference(subtrahend)
        # create a hash which contains all normalized keys
        all_pairs = (self.keys.map{|x| x.downcase} + subtrahend.keys.map{|x| x.downcase}).uniq.inject({}) do |pairs, key|
          pairs[key] = []
          pairs
        end
        #=> {"mouse"=>[], "cat"=>[], "snake"=>[], "bird"=>[], "dog"=>[]}
    
        # push original key value pairs into array which is the value of just created hash
        [self, subtrahend].each_with_index do |hsh, idx|
          hsh.each_pair { |k, v| all_pairs[k.downcase].push([k, v]) }
          all_pairs.each_value { |v| v.push([nil, 0]) if v.size == idx }
        end
        #=> {"mouse"=>[[nil, 0], ["Mouse", 75]], "cat"=>[["Cat", 100], ["cat", 50]], "snake"=>[["Snake", 10], ["Snake", 10]], "bird"=>[["Bird", 2], ["BIRD", 4]], "dog"=>[["Dog", 5], ["dog", 3]]}
    
        results = {}
        all_pairs.each_value do |values|
          diff = values[0][1] - values[1][1]
          # always take the key whose value is larger
          if diff > 0
            results[values[0][0]] = diff
          elsif diff < 0
            results[values[1][0]] = diff
          end
        end
        return results
      end
    end
    
    puts h1.difference(h2).inspect #=> {"Cat" => 50, "Dog" => 2, "BIRD" => -2, "Mouse" => -75}
    h1.difference(h2) == h3 ? puts("Pass") : puts("Fail") #=> "Pass"
    

    根据你的描述,这个做得很好。结果正是您所显示的(最终结果中的键未标准化,但取决于谁的值更大)。

    【讨论】:

      猜你喜欢
      • 2017-02-16
      • 1970-01-01
      • 1970-01-01
      • 2020-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-21
      • 2012-06-16
      相关资源
      最近更新 更多