Array.uniq does not use ==?

I believe that might do the trick, but it still seems like there should be a
more elegant solution, for my particular problem. Let me define my problem
a little more to help clarify.

My Agent is an object I wish to use in a genetic algorithm. Among other
things, it has attributes chromosome and fitness. I have “mixed in” the
Comparable module and defined my <=> operator so I can sort my Agents based
on their level of fitness.

However, I wish to grab “unique” agents based on their chromosomes as well.
Now, technically, if an agent has the same chromosome as another, it will
evaluate to the same fitness, but it is possible to have identicle fitness
with different chromosomes.

So, among the attributes of my agent, I wish to consider only the
chromosome when I look for uniqueness. I don’t need anything else. So,
basically, I was hoping to find an elegant “Ruby Way” style solution, where
all I need to do is override the Array.uniq, or override the operator for
which it derives its answer. However, I cannot seem to figure out how
exactly uniq is comparing objects.

And, actually, after taking a closer look at the solution you provided…
I’m not too sure I see how would work. Doesn’t .inspect also return the
Object.hash attribute of the object, which, unless defined otherwise, will
be different for every object? This would then make each object different,
even if all the other attributes are the same.

Matt

···

From: Mark Wilson mwilson13@cox.net
Reply-To: ruby-talk@ruby-lang.org
To: ruby-talk@ruby-lang.org (ruby-talk ML)
Subject: Re: array.uniq does not use == ?
Date: Tue, 10 Jun 2003 04:04:21 +0900

On Monday, June 9, 2003, at 02:43 PM, Orion Hunter wrote:

I have a user defined object, Agent. And I want to pass my Agents around
in an Array. I’ve “mixed-in” the Comparable module, and defined <=>
within the class for sorting purposes. However, I also want to be able
to perform Array.uniq/Array.uniq! as well, having the decision on the
“uniqueness” of the object derived from one particular attribute.

[snip]

I think the answer depends on the characteristics of the Agent object.

Can you use a Set to hold your Agents? Depending on what the class of the
Agent is, each instance might be treated as a separate object even if two
Agent objects have the same attributes (for example, this is true for Set
objects within a Set object). If this is the case, uniqueness on the basis
of attributes could be determined something like this:

Deletes objects with duplicate attributes from the calling set. Used to

deal with a

set object being able to contain two different set objects that have the

same elements.
def unique_attributes!
s =
self.each { |e|
if s.include?(e.inspect)
self.delete(e)
s.push(e.inspect)
else
s.push(e.inspect)
end
}
self
end

Note that you would need to separately define a unique_attributes to take
(set = self.dup) for the non-mutating version.

This method could also be adapted if you have to use Arrays and can’t use
Sets.

Other additional set methods are at:

http://www.rubygarden.org/ruby?AdditionalSetMethods

Regards,

Mark


STOP MORE SPAM with the new MSN 8 and get 2 months FREE*
http://join.msn.com/?page=features/junkmail

For Sets and Arrays, .inspect does not return the Object.hash
attribute. See below. I have not examined this behavior for all
object classes. Others on the list may be able to precisely explain
what is going and offer a more elegant solution.

irb(main):001:0> require ‘set’
=> true
irb(main):002:0> set = Set.new([1,2,3,4,5])
=> #<Set: {5, 1, 2, 3, 4}>
irb(main):003:0> set.inspect
=> “#<Set: {5, 1, 2, 3, 4}>”

array = [Set.new([1,2,3]), Set.new([4,5,6])]
=> [#<Set: {1, 2, 3}>, #<Set: {5, 6, 4}>]
irb(main):008:0> array.inspect
=> “[#<Set: {1, 2, 3}>, #<Set: {5, 6, 4}>]”

Regards,

Mark

···

On Monday, June 9, 2003, at 03:47 PM, Orion Hunter wrote:

[snip many interesting things I want to think about]

And, actually, after taking a closer look at the solution you
provided… I’m not too sure I see how would work. Doesn’t .inspect
also return the Object.hash attribute of the object, which, unless
defined otherwise, will be different for every object? This would
then make each object different, even if all the other attributes are
the same.

Matt
[snip]

Orion,

My Agent is an object I wish to use in a genetic
algorithm. Among other things, it has attributes
chromosome and fitness. I have “mixed in” the
Comparable module and defined my <=> operator so
I can sort my Agents based on their level of
fitness.

However, I wish to grab “unique” agents based on
their chromosomes as well.

The way Array#uniq works is basically:

class Array
def uniq
tmphash = {}
self.each { |e| tmphash[e] = true }
return self.dup if self.length == tmphash.length
tmparray =
self.each { |e| tmparray << e if tmphash.delete(e)}
tmparray
end
end

This means that you will want to override Agent#hash.  About the only

requirement for a hash key is that it must respond to #hash with a value
that does not change. See the Pickaxe book (The Ruby Language/The Basic
Types/Requirements for a Hash Key) for more information. You could probably
get away with hash returning “#{@chromosome}:#{@fitness}”.

In addition to adding Agent#hash, you will want to override Agent#eql?

to assert your definition of equality when comparing for uniqueness.

I hope this helps.

- Warren Brown