Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subtract two hashes in Ruby

Tags:

ruby

hash

Can the hash class be modified so that given two hashes, a new hash containing only keys that are present in one hash but not the other can be created?

E.g.:

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

h2 = {"Cat" => 100, "Dog" => 5, "Bison" => 30}

h1.difference(h2) = {"Bird" => 2, "Snake" => 10}

Optionally, the difference method could include any key/value pairs such that the key is present in both hashes but the value differs between them.

like image 402
Kyle Strand Avatar asked Jul 08 '14 21:07

Kyle Strand


3 Answers

h1 = {"Cat" => 100, "Dog" => 5, "Bird" => 2, "Snake" => 10}
h2 = {"Cat" => 999, "Dog" => 5, "Bison" => 30}

Case 1: keep all key/value pairs k=>v in h1 for which there is no key k in h2

This is one way:

h1.dup.delete_if { |k,_| h2.key?(k) }
  #=> {"Bird"=>2, "Snake"=>10}

This is another:

class Array
  alias :spaceship :<=>
  def <=>(o)
    first <=> o.first
  end
end

(h1.to_a - h2.to_a).to_h
  #=> {"Bird"=>2, "Snake"=>10}

class Array
  alias :<=> :spaceship
  remove_method(:spaceship)
end

Case 2: keep all key/value pairs in h1 that are not in h2

All you need for this is:

(h1.to_a - h2.to_a).to_h
  #=> {"Cat"=>100, "Bird"=>2, "Snake"=>10}

Array#to_h was introduced in Ruby 2.0. For earlier versions, use Hash[].

like image 98
Cary Swoveland Avatar answered Sep 17 '22 11:09

Cary Swoveland


Use the reject method:

class Hash
  def difference(other)
    reject do |k,v|
      other.has_key? k
    end
  end
end

To only reject key/value pairs if the values are identical (as per mallanaga's suggestion via a comment on my original answer, which I have deleted):

class Hash
  def difference(other)
    reject do |k,v|
      other.has_key?(k) && other[k] == v
    end
  end
end

like image 33
Kyle Strand Avatar answered Sep 20 '22 11:09

Kyle Strand


You can do this:

h2.each_with_object(h1.dup){|(k, v), h| h.delete(k)}
like image 43
sawa Avatar answered Sep 18 '22 11:09

sawa