# Fetching objects used as hash keys

**URL:** <https://www.ruby-forum.com/t/fetching-objects-used-as-hash-keys/84659>\
**Category:** Ruby\
**Created:** [February 6, 2007, 8:04pm UTC](https://www.ruby-forum.com/t/fetching-objects-used-as-hash-keys/84659 "2007-02-06T20:04:56Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [February 6, 2007, 8:04pm UTC](https://www.ruby-forum.com/t/fetching-objects-used-as-hash-keys/84659/1 "2007-02-06T20:04:56Z")

</div>

Hello

I’m using objects which are instances of a class that has 3 instance  
variables as hash keys, but I’m using the value of only two of those  
instance variables for the hash calculation. It’s something like this:

class A  
attr\_reader :x, :y, :z

def initialize(x, y)  
@x = x  
@y = y  
@z = rand  
end

def hash  
@x.hash ^ @y.hash  
end

def eql?(other)  
@x == other.x and @y == other.y  
end  
end

a1 = A.new(‘foo’, ‘bar’) # say @z = 0.25 here  
a2 = A.new(‘foo’, ‘bar’) # say @z = 0.90 here

h = {}  
h[a1] = true  
p h[a2] #=\> true

I did it this way because when I need to test the presence of an object  
in the hash, the value of @z is not known (and doesn’t matter). However,  
once I know the object is there, I’d like to fetch it, and check the  
value of @z of the object that was used as the hash key, that is, I’d  
like to get “0.25” in the example above.

What I had to do was use a hash of hashes instead, so I currently I have

h = {}  
x = ‘foo’  
h[x] = {  
:y =\> ‘bar’  
:z =\> rand  
}

So I’m guessing maybe a hash isn’t the correct data structure to do what  
I want here (i.e., fetch an object based on some of its attributes).  
Anyone has suggestions on how I could implement that, if possible  
keeping the efficiency of a hash access?

Thanks in advance,  
Andre

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [February 8, 2007, 3:36am UTC](https://www.ruby-forum.com/t/fetching-objects-used-as-hash-keys/84659/2 "2007-02-08T03:36:15Z")

</div>

On 6 feb, 16:04, Andre N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> @x = x
> 
> ```
> 
> end  
> end
> 
> a1 = A.new(‘foo’, ‘bar’) # say @z = 0.25 here  
> a2 = A.new(‘foo’, ‘bar’) # say @z = 0.90 here
> 
> h = {}  
> h[a1] = true  
> p h[a2] #=\> true

Okay, your explanation does not make much sense. But… if @z is not  
needed and you want it to be returned as the hash value, why is it in  
the class? Make _that_ the hash value, like:

h = {}  
h[a1] = rand  
p h[a2]

> > 0.25

should give you what you want. If you do want it in the class, then  
you don’t need a hash. Just doing a1.z should give you back what you  
want.  
If you need it in both, pass it around when you create it, like:

h = {}  
[a1, a2]. each { |key|  
z = h[key] || rand  
h[key] = z  
key.z = z # make z attr\_accessor, not attr\_reader of  
course  
}

Or did I miss something?

---

<div class="post-metadata">

**Author:** ![reflex](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/r/f05b48/32.png) [@reflex](https://www.ruby-forum.com/u/reflex)\
**Post date:** [February 8, 2007, 4:07pm UTC](https://www.ruby-forum.com/t/fetching-objects-used-as-hash-keys/84659/3 "2007-02-08T16:07:28Z")

</div>

On 2/6/07, Andre N. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> So I’m guessing maybe a hash isn’t the correct data structure to do what  
> I want here (i.e., fetch an object based on some of its attributes).  
> Anyone has suggestions on how I could implement that, if possible  
> keeping the efficiency of a hash access?

You probably want a set.

require “set”

class A  
attr\_reader :x, :y, :z

def initialize(x, y, z = rand)  
@x = x  
@y = y  
@z = z  
end

def hash  
@x.hash ^ @y.hash  
end

def eql?(other)  
@x == other.x and @y == other.y  
end  
end

a1 = A.new(‘foo’, ‘bar’) # say @z = 0.25 here  
puts “a1.z: #{a1.z}”  
a2 = A.new(‘foo’, ‘bar’) # say @z = 0.90 here  
puts “a2.z: #{a2.z}”

s = Set.new [a1]  
p s  
s.add(a2)  
p s  
p s.include?(a2)  
p s.find { |m| m.eql?(a2) }.z

# Make it nicer:

def s.[] (lookup)  
self.find {|elem| elem.eql?(lookup) }  
end

p s[a2].z

# Hope that helps

# pth
