Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hash subtraction in ruby

I have 2 hashes (that later will be converted to array, not sure if this is relevant):

Hash1 = {"X"=>2, "Y"=>1, "Z"=>1}

Hash2 = {"X"=>1, "Y"=>1}

I need to subtract them like (Hash3 = Hash1 - Hash2), and I need the result of Hash3 to be, in this case :

Hash3 = {"X"=>1, "Y"=>0, "Z"=>1}

All examples and answers I've seen led to results where the key which had value equals 0 (Y) to be absent in the resulting hash, which is not what I need.

I'm using Ruby 2.3.3 and Rails 5.0

like image 458
Lucas Oliveira Avatar asked Dec 01 '22 11:12

Lucas Oliveira


2 Answers

You can merge them:

h1 = {"X"=>2, "Y"=>1, "Z"=>1}
h2 = {"X"=>1, "Y"=>1}

h1.merge(h2) { |k, v1, v2| v1 - v2 }
#=> {"X"=>1, "Y"=>0, "Z"=>1}

Whenever a key is present in both hashes, the block is called to determine the new value.


Due to this behavior it will not result in negative values if a key is only present in h2:

h1 = {"X"=>2, "Y"=>1}
h2 = {"X"=>1, "Y"=>1, "Z"=>1}

h1.merge(h2) { |k, v1, v2| v1 - v2 }
#=> {"X"=>1, "Y"=>0, "Z"=>1}

You might expect:

#=> {"X"=>1, "Y"=>0, "Z"=>-1}

Which is what tadman's solution would return.

like image 184
Stefan Avatar answered Dec 19 '22 21:12

Stefan


It's not too difficult if you break it down into two steps:

def hash_sub(a, b)
  (a.keys + b.keys).uniq.map do |k|
    [ k, a[k].to_i - b[k].to_i ]
  end.to_h
end

hash_sub({"X"=>2, "Y"=>1, "Z"=>1}, {"X"=>1, "Y"=>1})
# => {"X"=>1, "Y"=>0, "Z"=>1}

The first step is to compute all the possible (unique) keys by combining the keys from the two hashes, and then convert to a new hash by subtracting one from the other, forcing conversion to an integer with .to_i.

like image 29
tadman Avatar answered Dec 19 '22 21:12

tadman