Comparing objects

How can I compare two objects and get true if some of his atributes are
equals ?

I need to compare two arrays of users, and get and third array just with
the matches. I found the “&” method that work for Fixnuns and String,
but…how to use with objects ?

class User
attr_accessor :email
end

a = User.new
a.email = ‘[email protected]

b = User.new
b.email = ‘[email protected]

array_one = [a]
array_two = [b]

array_three = array_one & array_two

puts array_three # I want the user here

Can you help me ?
thanks

Anderson L. wrote:

How can I compare two objects and get true if some of his atributes are
equals ?

I need to compare two arrays of users, and get and third array just with
the matches. I found the “&” method that work for Fixnuns and String,
but…how to use with objects ?

class User
attr_accessor :email
end

a = User.new
a.email = ‘[email protected]

b = User.new
b.email = ‘[email protected]

array_one = [a]
array_two = [b]

array_three = array_one & array_two

puts array_three # I want the user here

Can you help me ?
thanks

I think you need to define how to compare your objects and their hash.
Have a look below.

class User
attr_accessor :email

def ==(other)
@email == other.email
end

alias eql? ==

def hash
code = 17
code = 37*code + @email.hash
end

def to_s
“#@email
end

end

a = User.new
a.email = ‘[email protected]

c = User.new
c.email = ‘[email protected]

b = User.new
b.email = ‘[email protected]

d = User.new
d.email = ‘[email protected]

d = User.new
d.email = ‘[email protected]

array_one = [a,d]
array_two = [b, c]

array_three = array_one & array_two

puts array_three # I want the user here
#prints [email protected]

On Fri, Jun 4, 2010 at 3:41 AM, Marcin W. [email protected] wrote:

def ==(other)
@email == other.email
end
this seems ok

alias eql? ==

def hash
code = 17
code = 37*code + @email.hash
end

Are you sure you want to do this? “equal” objects would overwrite each
other when used as hash keys. This is normally not a good idea, be
sure you really want/need this!
R

On Fri, Jun 4, 2010 at 10:16 AM, Marcin W. [email protected] wrote:

�def hash
code will not work without overwritten eql? and hash methods.

Posted via http://www.ruby-forum.com/.

Does it not? Seems to work on my box
class A
def == other
true
end
end # class A

a1 = A::new
a2 = A::new
a3 = A::new

p [a1,a2] & [a1,a3]

Robert D. wrote:

On Fri, Jun 4, 2010 at 3:41 AM, Marcin W. [email protected] wrote:

�def ==(other)
�@email == other.email
�end
this seems ok

�alias eql? ==

�def hash
� �code = 17
� �code = 37*code + @email.hash
end

Are you sure you want to do this? “equal” objects would overwrite each
other when used as hash keys. This is normally not a good idea, be
sure you really want/need this!
R

What would be the other possible solution to this problem? The example
code will not work without overwritten eql? and hash methods.

2010/6/4 Marcin W. [email protected]:

�def hash
� �code = 17
� �code = 37*code + @email.hash
end

Are you sure you want to do this? “equal” objects would overwrite each
other when used as hash keys. This is normally not a good idea, be
sure you really want/need this!

Absolutely agree!

What would be the other possible solution to this problem?

Actually I am not sure what the problem actually is. Does OP want to
return all objects which are in both Arrays? Does he want to return
all objects which share a particular set of properties? etc. Before
we can provide solutions we have to know what problem must be solved.

Kind regards

robert

Before

we can provide solutions we have to know what problem must be solved.

Kind regards

robert

I have a list of objects that came from database and another list of
objects extracted from a xml. I need the elements who are in both lists.

Then…I thought to compare objects overriding the == method like Marcin
Wolski wrote. There is another solution ?

On 05.06.2010 00:35, Anderson L. wrote:

Before

we can provide solutions we have to know what problem must be solved.

I have a list of objects that came from database and another list of
objects extracted from a xml. I need the elements who are in both lists.

Then…I thought to compare objects overriding the == method like Marcin
Wolski wrote. There is another solution ?

If all objects you are dealing with implement #eql? and #hash in a way
to be suitable for that comparison then using #eql? is the most
straightforward approach.

If they do not and you want to do the comparison based on other criteria
then you need to pick a different solution. For example:

in_both = obj_from_db.select do |u1|
obj_from_xml.any? do |u2|
u1.email == u2.email && u1.age == u2.age
end
end

The larger set should be used for the outer iteration. If both sets are
really large then you probably rather want to use a different strategy
by speeding up access via a Hash. For that you need a specific Hash key
(remember, we had assumed #eql? and #hash cannot be used). That key
must implement #eql? and #hash; the easiest way to get such a Key class
is to use Struct:

Key = Struct.new :email, :age

db_index = {}

obj_from_db.each do |u1|
db_index[Key[u1.email, u1.age]] = u1
end

in_both = obj_from_xml.select do |u2|
db_index[Key[u2.email, u2.age]]
end

Kind regards

robert

On Sat, Jun 5, 2010 at 2:35 PM, Robert K.
[email protected] wrote:

Then…I thought to compare objects overriding the == method like Marcin
Wolski wrote. There is another solution ?

If all objects you are dealing with implement #eql? and #hash in a way to be
suitable for that comparison then using #eql? is the most straightforward
approach.
Not #eql?, #== please.
Cheers
R.

Correction:

def hash
@email.hash ^ self.class.hash +
end

should be:

def hash
@email.hash ^ self.class.hash + 1
end

On Sat, Jun 5, 2010 at 8:55 PM, Rein H. [email protected] wrote:

The Array#& operator, like its siblings, uses the #hash and #eql? methods of
the objects it compares. These must be the same for objects you want to be
considered equivalent.

I must have made a stupid mistake in my example code, however, this is
no reason to mess with #eql? and #hash, Be warned.
In that case I stick with Robert’s advice: Do not use Array#&, even
MPing Array with something like #intersect_by would be preferable.

Cheers
Robert

The first solution sent by Marcin W. works for normal ruby classes,
but I got something wrong when using with an ActiveRecord class. I am
not sure about the problem know.

So I tried Robert K.'s solution, using:

in_both = obj_from_db.select do |u1|
obj_from_xml.any? do |u2|
u1.email == u2.email && u1.age == u2.age
end
end

That’s working know.
I have to study a little more of your suggestions since this aproach
still looks like slowly yet…

To figure the problem, I am using this algorithm at
http://www.dobbyme.com

That’s a birthday reminder. The idea implemented here is: I got your
gmail contacts (useim gdata gem) and lokk at dobbyme’s database to match
if you have some contact already registered.

I think now the actual aproach is fine, but with a lot of users I will
start to have some troubles…am I right ? Opnions ?

On 2010-06-05 13:09:30 -0700, Robert D. said:

Cheers
Robert

“Be warned”? “Do not use Array#&”? Enough of this baseless FUD.
Understand the consequences of what you’re doing, certainly, but don’t
shy away from techniques just because someone else cries wolf.

Array#& is meant to be used. Implementing #hash and #eql? in your
classes is as normal as implementing #==, #<=> or #each. Ruby’s duck
typing is one of its most powerful features and should always be
preferred over one-off monkey-patching. I’m afraid Robert has it
precisely backwards.

First, let’s be clear about the semantics of the various comparison
methods:

The == operator checks whether two objects are equal (after coercing
them to the same type).

1 == 2 # => false
1 == 1.0 # => true

#eql? checks if two objects are equal and of the same type.

1.eql? 1.0 # => false
‘a’.eql? ‘a’ # => true

#equal? checks if two objects are identical (the same object).

‘a’.equal? ‘a’ # => false
:a.equal? :a # => true

As a mnemonic, note that their strictness is a function of their length.

Now, on to the original question, which (paraphrased) is:

“How can I get arrays of custom objects to work as expected with the
set intersection operator &?”

The Array#& operator, like its siblings, uses the #hash and #eql?
methods of the objects it compares. These must be the same for objects
you want to be considered equivalent.

Armed with this knowledge, let’s define those methods on User. For
instance:

class User

checks if objects are equal

def ==(other)
@email == other.email
end

checks if objects are equal and the same class

def eql?(other)
self == other && self.class == other.class
end

unique hash based on email and class

We add 1 to prevent User.new(‘’).hash == User.hash

NOTE: Users with @email == nil will have the same hash and modify

if necessary.
def hash
@email.hash ^ self.class.hash +
end
end

This will allow:

[User.new(‘[email protected]’)] & [User.new(‘[email protected]’)]
=> [#<User:0x1016dcdb8 @email=“[email protected]”>]

Which I believe was the original intent. Keep in mind that this will
also cause Hash to see them as the same object for keys, which is
probably also expected

As an aside, I would also like to comment that the following, from one
of the responses:

def to_s
“#@email
end

is unnecessarily complex and can be replaced simply with:

def to_s
@email
end

Don’t use string interpolation when you don’t need it. If you want to
ensure that @email is a string, call to_s on it instead.

On 2010-06-03 15:29:55 -0700, Anderson L. said:

a = User.new

puts array_three # I want the user here

Can you help me ?
thanks

Rein H.

http://reinh.com

Robert D. wrote:

On Sat, Jun 5, 2010 at 8:55 PM, Rein H. [email protected] wrote:

The Array#& operator, like its siblings, uses the #hash and #eql? methods of
the objects it compares. These must be the same for objects you want to be
considered equivalent.

I must have made a stupid mistake in my example code, however, this is
no reason to mess with #eql? and #hash, Be warned.
In that case I stick with Robert’s advice: Do not use Array#&, even
MPing Array with something like #intersect_by would be preferable.

Wow. Sorry, I am just stunned by this, so no sugar coating for you
today.

Array#& is a perfectly legitimate method to be using for the purpose of
finding the intersect of two Array objects, which it seems what the OP
wanted to do originally.

I agree with Rein on this.

On Sat, Jun 5, 2010 at 4:09 PM, Robert D. [email protected]
wrote:

I must have made a stupid mistake in my example code, however, this is
no reason to mess with #eql? and #hash, Be warned.
In that case I stick with Robert’s advice: Do not use Array#&, even
MPing Array with something like #intersect_by would be preferable.

Implementing this behavior with your own object types is the whole
point of the Ruby core API.
I find myself struggling to understand the mindset that would prefer
re-opening Array and adding something over using the
already-there-for-a-reason Enumerable and Array interfaces.

Using eql? and hash is not ‘messing with’ them, unless you don’t take
the time to understand your tools.

On Sun, Jun 6, 2010 at 1:18 AM, Wilson B. [email protected]
wrote:

already-there-for-a-reason Enumerable and Array interfaces.
Really? What do we want to do? We want Array#& work with a tailor made
comparision.
I accept that attitude, although I would not recommend it.
Now I would expect that the responsibility for changing the semantics
of an Array method should be in the Array class, or a module closely
related to class. As Array#& needs to invoke client object’s methods,
we have to change those. And reality is that Hash methods invoke those
same client methods. Thus I would not say that #hash? and #eql? are
already here for the reason you stated.

Using eql? and hash is not ‘messing with’ them, unless you don’t take
the time to understand your tools.
I agree that was bad style from my side, sorry. However I maintain
that the context does not really justify their modifications as the
semantic impact is much greater than wanted, unless of course OP wants
hashes to believe exactly as they would after the aforementioned
modifications.

Cheers
Robert

On Sun, Jun 6, 2010 at 1:05 AM, Ryan B. [email protected] wrote:

MPing Array with something like #intersect_by would be preferable.

Wow. Sorry, I am just stunned by this, so no sugar coating for you
today.

Array#& is a perfectly legitimate method to be using for the purpose of
finding the intersect of two Array objects, which it seems what the OP
wanted to do originally.

Did I say you should not use Array#&??? I do not think so. I said, do
not overload Object#hash and Object#eql? for the purpose to use
Array#&. And if I was not clear enough I will try again: It will brake
any client code that uses your objects as hash keys.
Thus you override #hash and #eql? if you want that behavior only.
But yes, using Array#& with different object comparison semantics
seems a bad idea to me. Please bear in mind that there is no reason to
assume that OP has the kind of experience to assume the side effects
present (He would not have asked the question in that case).
Cheers
Robert

Robert D. wrote:

On Sun, Jun 6, 2010 at 1:05 AM, Ryan B. [email protected] wrote:

MPing Array with something like #intersect_by would be preferable.

Wow. Sorry, I am just stunned by this, so no sugar coating for you
today.

Array#& is a perfectly legitimate method to be using for the purpose of
finding the intersect of two Array objects, which it seems what the OP
wanted to do originally.

Did I say you should not use Array#&??? I do not think so.

Let me refresh your memory:

“In that case I stick with Robert’s advice: Do not use Array#&,”

On 05.06.2010 16:41, Robert D. wrote:

Wolski wrote. There is another solution ?
If all objects you are dealing with implement #eql? and #hash in a way to be
suitable for that comparison then using #eql? is the most straightforward
approach.
Not #eql?, #== please.

Why? Array#& uses #eql? - as Hash lookup methods do, too.

$ ruby19 -e ‘class C;def eql?(x)printf “eq %p\n”,x;false;end;def
hash;0;end;end;[C.new] & [C.new]’
eq #<C:0x10028284>

Kind regards

robert