Reputation: 169
I'm going mad trying to resolve a key/value issue on a simple hash inside a hash.
My key is of type "OpenStudio::OptionalString" which comes from an API used in my code:
#---NOTE---: key here is of type OpenStudio::OptionalString
my_hash[key]['heating_plant_system'] = 'Boiler'
my_value = my_hash[key]['heating_plant_system'] #returning nil
In debug mode I inspected the hash and saw that the first line correctly entered the key/value input, but I am unable to retrieve the value when I run the second line. my_value
would return nil. I know it's due to this odd key type, but I'm not at liberty to change it.
Am I making a dumb mistake when trying to access this value from my hash?
In an attempt to keep things general, I may have sacrificed too much context. This is a complete example:
require 'openstudio'
model = OpenStudio::Model::Model.new
my_zone = OpenStudio::Model::ThermalZone.new(model)
my_zone.setName('Zone 1')
zone_hash = Hash.new { |h, k| h[k] = { } }
zone_hash[my_zone.name]['heating_plant'] = 'Boiler'
puts "my zone's name is #{my_zone.name}" #Output: 'my zone's name is Zone 1'
puts zone_hash.to_s #Output: {#<OpenStudio::OptionalString:0x5fa4980 @__swigtype__="_p_boost__optionalT_std__string_t">=>{"heating_plant"=>"Boiler"}}
if zone_hash[my_zone.name]['heating_plant'].nil?
puts 'Im unable to access this hash, help!' #<--- this is executed
else
puts "I am able to access #{zone_hash[my_zone.name]['heating_plant']}"
end
Since I could not (easily) undo how this zone_hash
worked in my actual code base by changing the key to something other than OpenStudio::OptionalString, I used this loop as a work around. It's not pretty, but it got the job done for the small checks I needed to do:
zones_hash.each {|k,v|
if zone.name.to_s == k.to_s
v.each {|k1,v1|
if k1 == 'heating_plant'
heating_plant = v1.to_s
end
}
end
}
Upvotes: 1
Views: 4395
Reputation: 160549
A hash key can be any object type as long as the objects are unique. The definition for uniqueness or equality is found in "Hash Keys".
Meditate on this:
v1 = %w(a b)
v2 = %w(c d)
hash = {v1 => 1, v2 => 2}
hash # => {["a", "b"]=>1, ["c", "d"]=>2}
hash[v1] # => 1
hash[%w(a b)] # => 1
As long as the key is unique you can use it:
class Foo
end
foo1 = Foo.new
foo2 = Foo.new
foo1.hash # => 1202274030892197226
foo2.hash # => 2774925608615277787
hash = {foo1 => 1, foo2 => 2}
hash[foo1] # => 1
Or even:
class Foo
def initialize
@init_time = Time.now
end
def init_time
@init_time
end
end
foo1 = Foo.new
foo2 = Foo.new
foo1.init_time.to_f # => 1484874655.324574
foo2.init_time.to_f # => 1484874655.324577
hash = {foo1.init_time => 1, foo2.init_time => 2}
hash[foo1.init_time] # => 1
Not quite true, Ruby uses hash and eql? for hash equality, it just so happens that the default implementation relies on object_id
You're right, it's been a long time since I cared exactly why they were unique. From the docs:
Two objects refer to the same hash key when their
hash
value is identical and the two objects areeql?
to each other.
Upvotes: -1
Reputation: 27793
Ruby uses hash
and eql?
to check the equality of hash keys.
Looks like OpenStudio::OptionalString
might not correctly implement those. If that is the case your best solution is using another key.
Ruby makes the following assumptions for hash keys—if two objects are to be considered the same key they must return the same hash
value, however having the same hash value does not mean they are the same key. The method eql?
is used internally to resolve these cases.
You could also fix the hash
and eql?
methods on the OpenStudio::OptionalString
class, but maybe that library relies on the "broken" behavior internally. That is why I would recommend just using another hash key like for example a string representation of those objects.
Upvotes: 4