Reputation: 16499
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.
Upvotes: 13
Views: 11038
Reputation: 11631
For deep nesting you can add a bit of recursion, something like (untested)
class Hash
def -(h2)
raise ArgumentError unless h2.is_a?(Hash)
h1 = dup
h1.delete_if do |k, v|
if v.is_a?(Hash) && h2[k].is_a?(Hash)
h1[k] = v - h2[k]
h1[k].blank?
else
h2[k] == v
end
end
end
end
end
Upvotes: 0
Reputation: 16499
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
Upvotes: 10
Reputation: 110675
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[].
Upvotes: 16
Reputation: 168101
You can do this:
h2.each_with_object(h1.dup){|(k, v), h| h.delete(k)}
Upvotes: 2