Customizing Array#uniq by defining eql?

  • Thread starter Marcel Molina Jr.
  • Start date
M

Marcel Molina Jr.

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
eql? called
=> true
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
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: 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?+.

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.
 
M

Marcel Molina Jr.

In message "Re: 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?+.

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.

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
 
D

Daniel Berger

Marcel said:
In message "Re: Customizing Array#uniq by defining eql?"
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.

I agree that this is misleading. Can we clarify this in the
documentation please?

Regards,

Dan
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: Customizing Array#uniq by defining eql?"

|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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top