2

I cannot find a way to determine if a key-value pair exists in a hash.

h4 = { "a" => 1, "d" => 2, "f" => 35 } 

I can use Hash#has_value? and Hash#has_key? to find information about a key or a value individually, but how can I check if a pair exists?

Psuedo-code of what I'm after:

if h4.contains_pair?("a", 1) 
2
  • Can the value possibly be nil? Commented Mar 15, 2016 at 0:11
  • Thanks for the note sawa! Jordan already addressed both scenarios. Commented Mar 15, 2016 at 0:18

3 Answers 3

5

Just use this:

h4['a'] == 1 

It seems excessive to me, but you could monkey-patch Hash with a method like so:

class Hash def contains_pair?(key, value) key?(key) && self[key] == value end end 
Sign up to request clarification or add additional context in comments.

3 Comments

Gah, I thought you could only set hash values with that syntax--didn't think to try ==. I wish you wouldn't have seen this thread Jordan... now you know how much of a noob I really am. I'll mark your answer as correct shortly as it is the most concise.
Jordan, are you by chance available to glance at stackoverflow.com/questions/39951060/… pretty please? I'm hitting a wall on an important project to me and so far after a couple days I don't have any reaction on SO.
@sawa's comment on the question was alluding to the fact that h[k] == v does not work if v can equal nil.
3

I confess to starting down a road and then wondering where it might take me. This may not be the best way of determining if a key/value pair is present in a hash (how could one improve on @Jordan's answer?), but I learned something along the way.

Code

def pair_present?(h,k,v) Enumerable.instance_method(:include?).bind(h).call([k,v]) end 

Examples

h = { "a"=>1, "d"=>2, "f"=>35 } pair_present?(h,'a',1) #=> true pair_present?(h,'f',36) #=> false pair_present?(h,'hippopotamus',2) #=> false 

Discussion

We could of course convert the hash to an array and then apply Array#include?:

h.to_a.include?(['a', 1]) #=> true 

but that has the downside of creating a temporary array. It would be nice if the class Hash had such an instance method, but it does not. One might think Hash#include? might be used for that, but it just takes one argument, a key.1.

The method Enumerable#include? does what we want, and of course Hash includes the Enumerable module. We can invoke that method in various ways.

Bind Enumerable#include? to the hash and call it

This was of course my answer:

Enumerable.instance_method(:include?).bind(h).call([k,v]) 

Use the method Method#super_method, which was introduced in v2.2.

h.method(:include?).super_method.call(['a',1]) #=> true h.method(:include?).super_method.call(['a',2]) #=> false 

Note that:

h.method(:include?).super_method #=> #<Method: Object(Enumerable)#include?> 

Do the alias_method/remove_method merry-go-round

Hash.send(:alias_method, :temp, :include?) Hash.send(:remove_method, :include?) h.include?(['a',1]) #=> true h.include?(['a',2]) #=> false Hash.send(:alias_method, :include?, :temp) Hash.send(:remove_method, :temp) 

Convert the hash to an enumerator and invoke Enumerable#include?

h.to_enum.include?(['a',1]) #=> true h.to_enum.include?(['a',2]) #=> false 

This works because the class Enumerator also includes Enumerable.

1 Hash#include? is the same as both Hash#key? and Hash#has_key?. It makes me wonder why include? isn't used for the present purpose, since determining if a hash has a given key is well-covered.

1 Comment

Whoa, good stuff. I am glad I asked the question instead of shying away from it because I thought it was too simple. This is really good stuff, thanks Cary!
0

How about using Enumerable any?

h4 = { "a" => 1, "d" => 2, "f" => 35 } h4.any? {|k,v| k == 'a' && v == 1 } 

1 Comment

It seems a little bizarre to iterate over the entire Hash when we can access the key directly in O(1) time.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.