6

我希望能够减去两个哈希并在 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 方法能够工作。换句话说,我希望“猫”与“猫”相匹配。

这是我到目前为止所拥有的:

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

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

任何帮助,将不胜感激。

4

5 回答 5

4

如何将哈希转换为集合。

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]}>
于 2012-08-14T18:57:13.943 回答
3

作为推荐...

我过去用过这样的东西:

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”),则会遇到问题,因为规范化会踩到一个。

于 2012-08-14T19:42:26.163 回答
0

我得到了这个resque 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} 
于 2012-08-14T19:28:18.293 回答
0

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

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"
于 2012-08-14T19:08:59.140 回答
0

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

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"

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

于 2012-08-17T11:53:14.687 回答