From: Brian Candler Date: 2007-05-09T20:07:27+09:00 Subject: Re: Equality question On Wed, May 09, 2007 at 06:05:11PM +0900, Robert Klemme wrote: > On 08.05.2007 18:55, Brian Candler wrote: > >On Wed, May 09, 2007 at 12:58:01AM +0900, Sebastian Hungerecker wrote: > >>Brian Candler wrote: > >>>o1 = [:defuzz_configs, {"conf"=>"foo.txt"}] > >>>o2 = [:defuzz_configs, {"conf"=>"foo.txt"}] > >>>p o1 == o2 # prints true > >>>p o1.hash == o2.hash # prints false ?? <<<<<<<<<<< > >>That's because the Hashs in o1 and o2 are two different objects. > > > >So hashes and arrays don't behave the same in this regard? > > > >o1 = ["hello", ["world"]] > >o2 = ["hello", ["world"]] > >p o1 == o2 > >p o1.hash == o2.hash # prints true > > > >o1 = {"key"=>"hello"} > >o2 = {"key"=>"hello"} > >p o1 == o2 > >p o1.hash == o2.hash # prints false > > > >I wonder why this is? > > Probably because Hashes store not only key value pairs but also default > value and an optionally block that is invoked if an element is missing > from the Hash. OK, then I'm missing some subtlety between hash1 == hash2 (which is true), and hash1.eql? hash2 (which is false). But given just arrays, and arrays of arrays, array1 == array2 and array1.eql? array2 are both true. And so is array1.hash == array2.hash So it appears that someone has decided that hashes cannot be used usefully as hash keys, but I cannot see why. > The solution > is of course to use your own class for your keys. Since they seem to be > pretty complex that's a good idea anyway. I solved my problem by doing a linear search over the hash to find the entry, which seems silly, but it works. # Was: return @output[input] @output.each { |(k,v)| return v if k == input } Making "my own class" was not an option. I was building a small mock object, and the hash key is whatever method_missing receives. If the caller of the mock object passes in a hash, then a hash is what I get. class Mock def self.prepare(output) @output = output end def self.[]=(input,output) @output[input] = output end def self.[](input) @output.each { |(k,v)| return v if k == input } raise "No response for #{input.inspect}" end attr_reader :actions def initialize(*args) @actions = [[:initialize] + args] end def method_missing(*args) # STDERR.puts "#{args.inspect} => #{self.class[args].inspect}" @actions << args self.class[args] end end Mock.prepare({ [:flurble] => 'boo', }) a = Mock.new(123) res = a.flurble p res p a.actions Yes, I know there are other mock solutions out there. I would rather just write something which is (a) small, and (b) does exactly what I need. Regards, Brian.