comparing objects

A

Anderson Leite

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 = '(e-mail address removed)'

b = User.new
b.email = '(e-mail address removed)'

array_one = [a]
array_two =


array_three = array_one & array_two

puts array_three # I want the user here



Can you help me ?
thanks
 
M

Marcin Wolski

Anderson said:
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 = '(e-mail address removed)'

b = User.new
b.email = '(e-mail address removed)'

array_one = [a]
array_two =


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 = '(e-mail address removed)'

c = User.new
c.email = '(e-mail address removed)'

b = User.new
b.email = '(e-mail address removed)'

d = User.new
d.email = '(e-mail address removed)'

d = User.new
d.email = '(e-mail address removed)'

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


array_three = array_one & array_two

puts array_three # I want the user here
#prints (e-mail address removed)
 
R

Robert Dober

=A0def =3D=3D(other)
=A0@email =3D=3D other.email
=A0end this seems ok

=A0alias eql? =3D=3D

=A0def hash
=A0 =A0code =3D 17
=A0 =A0code =3D 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


--=20
The best way to predict the future is to invent it.
-- Alan Kay
 
M

Marcin Wolski

Robert said:
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.
 
R

Robert Dober

What would be the other possible solution to this problem? The example
code will not work without overwritten eql? and hash methods.
Does it not? Seems to work on my box
class A
def =3D=3D other
true
end
end # class A

a1 =3D A::new
a2 =3D A::new
a3 =3D A::new

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



--=20
The best way to predict the future is to invent it.
-- Alan Kay
 
R

Robert Klemme

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

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
A

Anderson Leite

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 ?
 
R

Robert Klemme

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
 
R

Robert Dober

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?, #=3D=3D please.
Cheers
R.
--=20
The best way to predict the future is to invent it.
-- Alan Kay
 
R

Robert Dober

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
 
A

Anderson Leite

The first solution sent by Marcin Wolski 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 Klemme'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 ?
 
R

Ryan Bigg

Robert said:
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.
 
W

Wilson Bilkovich

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

Robert Dober

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
 
R

Robert Dober

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

Robert Klemme

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
 
R

Ryan Bigg

Robert said:
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#&,"
 
B

Benoit Daloze

[Note: parts of this message were removed to make it a legal post.]

Hi,

Just wanting to add my thoughts about this (I made a thread about this a few
months ago).

I searched a bit and concluded this:

Array methods using comparison
- with #hash and #eql?
&, |, uniq(!), -
- with #==
include?, (r)assoc, count, delete, (r,find_)index
(please say me if I forgot one)

I think Array methods should never have to look at #hash and #eql? methods.
I suppose this is done for performance.

I think this should change, because:
- it violates POLS
- it can make unexpected behavior because you defined #hash and #eql? , for
objects which should not need that (when you manage objects in an Array, you
do not expect to need to think about Hash's keys).
- it is not consistent with other Array's methods

PS: Rein: I saw your implementation of #hash. I think to "add one" is
useless, because #eql? is always used (so even if #hash was always the same,
it would work). It could maybe speed up a bit, but only if you have a lot of
comparison of User and User's instances, which is very unlikely.
 
W

Wilson Bilkovich

I hope that other Rubyists that may stumble upon this thread will take
Robert's FUD with a grain of salt and will feel free to determine the
usefulness and any potential dangers of implementing #eql? and #hash --
along with other Ruby idioms like #each (for Enumerable) and #<=> (for
Comparable) -- on their own. An ounce of critical thinking is better than a
pound of dogma.

Let me be clear, People of the Future: implement eql?, ===, and hash
on your own classes as appropriate. Doing so is the proper way to
allow your code to interact with other libraries and coders. Even if
your code lives in isolation, ensuring proper semantics via these
methods prevents a class of tricky bug that your successors may have
to deal with.
 
R

Robert Dober

your code lives in isolation, ensuring proper semantics via these
methods prevents a class of tricky bug that your successors may have
to deal with.
Hmm? Would you care to show an example where overloading those methods
(#eql? and #hash) is needed to ensure proper behavior? I am willing to
learn. But I am not willing to accept this statement as such.
Cheers
R.
 

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,777
Messages
2,569,604
Members
45,202
Latest member
MikoOslo

Latest Threads

Top