Customizing Array#uniq by defining eql?

PickAxe II's documentation for Array#uniq says,

    Returns a new array by removing duplicate values in _arr_, where
    duplicates are detected by comparing using +eql?+.

Given that say I have a simple class:

    class PossibleTime
      attr_reader :day, :start_time, :end_time

      def initialize(day, start_time, end_time)
        @day, @start_time, @end_time = day, start_time, end_time
      end

      def hash
        "#@day #@start_time #@end_time"
      end

      def ==(other_pt)
        self.hash == other_pt.hash
      end

      def eql?(other_pt)
        self == other_pt
      end
    end

Here +eql?+ is defined in terms of +==+. Creating two example instances shows
these methods working:

    > a = PossibleTime.new('M', '9:30', '10:30')
    > b = PossibleTime.new('M', '9:30', '10:30')
    > c = PossibleTime.new('T', '9:30', '12:00')
    > a == b
    => true
    > a.eql? b
    => true
    > a == c
    => false
    > [a,b].uniq.size
    => 2
    > # I was expecting 1, not 2

I'll add debugging to see if +eql?+ was even called:

    class PossibleTime
      def eql?(other_pt)
        puts 'eql? called'
        self == other_pt
      end
    end

    > a.eql? b
    eql? called
    => true
    > a.eql? c
    eql? called
    => false
    > [a,b].uniq.size
    => 2

So eql? isn't even being called.

Am I overlooking something? Specifying something incorrectly? Not
understanding the fundamental interaction between eql? and uniq?

Thanks for your time,
marcel

···

--
Marcel Molina Jr. <marcel@vernix.org>

Hi,

···

In message "Re: Customizing Array#uniq by defining eql?" on Tue, 8 Mar 2005 05:16:17 +0900, "Marcel Molina Jr." <marcel@vernix.org> writes:

PickAxe II's documentation for Array#uniq says,

   Returns a new array by removing duplicate values in _arr_, where
   duplicates are detected by comparing using +eql?+.

It creates hash internally to remove redundant values, so that
comparison is done by "eql?" but it is filtered by "hash" value first.
In other words, when you redefine "eql?" you have to redefine "hash"
as well.

              matz.

Thanks for the reply. Yes, indeed, I had noted that I needed to define
both hash and eql?. I assumed that that was all that was needed for
uniq to work as expected.

The pertinent bits of code from the example I posted in the original email
are as follows:

      def hash
        "#@day #@start_time #@end_time"
      end

      def ==(other_pt)
        self.hash == other_pt.hash
      end

      def eql?(other_pt)
        self == other_pt
      end

And yet eql? doesn't even seem to be called.

Thanks for your time,
marcel

···

On Tue, Mar 08, 2005 at 10:21:00AM +0900, Yukihiro Matsumoto wrote:

In message "Re: Customizing Array#uniq by defining eql?" > on Tue, 8 Mar 2005 05:16:17 +0900, "Marcel Molina Jr." <marcel@vernix.org> writes:

>PickAxe II's documentation for Array#uniq says,
>
> Returns a new array by removing duplicate values in _arr_, where
> duplicates are detected by comparing using +eql?+.

It creates hash internally to remove redundant values, so that
comparison is done by "eql?" but it is filtered by "hash" value first.
In other words, when you redefine "eql?" you have to redefine "hash"
as well.

--
Marcel Molina Jr. <marcel@vernix.org>

Hi,

···

In message "Re: Customizing Array#uniq by defining eql?" on Tue, 8 Mar 2005 11:32:37 +0900, "Marcel Molina Jr." <marcel@vernix.org> writes:

Thanks for the reply. Yes, indeed, I had noted that I needed to define
both hash and eql?. I assumed that that was all that was needed for
uniq to work as expected.

hash method should return integer value.

              matz.