6

Ruby で 2 つのハッシュを減算して 3 つ目のハッシュを取得できるようにしたいと考えています。

2 つのハッシュは次のようになります。

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}

両方のハッシュのキーと新しいハッシュの値を使用して新しいハッシュを作成し、最初のハッシュのキーの値から2番目のハッシュのそのキーの値を引いたものにしたいと思います。問題は、キーの大文字と小文字に関係なく、この 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」などの2つの一意の大文字と小文字のキーが含まれていると、正規化によって1つが踏みにじられるため、問題が発生します。

于 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

This time I would like to provide another solution: normalized -> store original key value pairs -> grab the original key who has larger value as the key for the difference.

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"

According to what you described, this one does a pretty good job. The result is exactly what you've shown (key is not normalized in the final result, but depends on whose value is bigger).

于 2012-08-17T11:53:14.687 に答える
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 に答える