On Wed, 7 Nov 2001 puzzled186 / hotmail.com wrote:

> I'd like to be able to do something like:
> {1..100=>"a",101..300=>"c", 500..1000=>"r"}
>
> The syntax accepts it, but it doesn't behave the way I'd like (always
> comes up nil).

As matju pointed out last night on #ruby-lang, Range does not override
Kernel#hash and Kernel#eql?.  The default behavior for an object that does
not override these functions is for the hash value to be the object id and
for eql? to test for object equality (same object), not value equality
(same value).  The following demonstrates the problem:

  h = {1..100=>"a",101..300=>"c", 500..1000=>"r"}
  p h[1..100] #=> nil

  class Range
    def hash
      # Is this a good hash function?
      self.begin + (11 * self.end) + (17 * (self.exclude_end? ? 23 : 29))
    end
  end
  h = {1..100=>"a",101..300=>"c", 500..1000=>"r"}
  p h[1..100] #=> nil

  class Range
    def eql?(other)
      self == other
    end
  end
  h = {1..100=>"a",101..300=>"c", 500..1000=>"r"}
  p h[1..100] #=> "a"

Is this fixed in 1.7?

I suppose the moral of the story is that if you write a class that you
expect to be able to put into a Hash, then you should override hash() and
eql?.  It's not enough to override hash() and ==, though:

  class Foo
    def initialize(x)
      @x = x
    end
    def hash()
      @x
    end
    def ==(other)
      @x == other.x
    end
    attr_reader :x
  end

  h = Hash[Foo.new(1) => 1, Foo.new(2) => 2, Foo.new(3) => 3]
  p h[Foo.new(1)] #=> nil

  class Foo
    def eql?(other)
      self == other
    end
  end
  h = Hash[Foo.new(1) => 1, Foo.new(2) => 2, Foo.new(3) => 3]
  p h[Foo.new(1)] #=> 1

Paul