Comparing objects

How do I compare two objects in Ruby, considering only attributes
values?

For example:

class Foo
attr_accessor :bar, :baz
end

lorem = Foo.new
ipsum = Foo.new

lorem.bar = 1
lorem.baz = 2

ipsum.bar = 1
ipsum.baz = 2

puts lorem == ipsum # false

Both objects belongs to the same class and also have the same attributes
values.

Thanks.

You can define the method <=> to allow comparison between objects.

def <=> other
bar <=> other.bar
end

or maybe something like this?

def <=> other
( bar + baz ) <=> ( other.bar + other.baz )
end

I see Joel P…

Why <=> instead of ==?

Thanks Joel P…

I should mention that you’d also have to include Comparable to inherit
the rest of the operators.
The method <=> returns -1, 0, or 1; which the other methods can use for
their responses.

Because that method gives you these all in one:

<, <=, ==, >=, >, and between?

Do you have any reference about these operators? I’d like to read about
it.
Thanks.

Here’s the Ruby 2.0 documentation for it.
http://ruby-doc.org/core-2.0/Comparable.html

On Mon, Jun 17, 2013 at 6:02 PM, Thom T. [email protected] wrote:

Do you have any reference about these operators? I’d like to read about
it.
Thanks.


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

Here’s a great article about all these methods you can implement and
their usage:

http://blog.rubybestpractices.com/posts/rklemme/018-Complete_Class.html

Jesus.

On Mon, Jun 17, 2013 at 10:52 AM, Thom T. [email protected] wrote:

ipsum = Foo.new
values.

Thanks.

I’d do it like this

class Foo
attr_accessor :bar, :baz
def ==(foo)
foo.kind_of?(self.class) && bar == foo.bar && baz == foo.baz
end
end

Only define <=> if your foos are ordered.

Josh C. wrote in post #1112674:

Only define <=> if your foos are ordered.

This is why specific examples are useful. Since numbers were shown as
the example I went for the best option when dealing with numbers :slight_smile:

I assumed that the OP’s use of “compare” meant Comparable, rather than
simply testing for equality.
As always, I reserve the right to be wrong :stuck_out_tongue:

Am 17.06.2013 17:56, schrieb Joel P.:

You can define the method <=> to allow comparison between objects.
[…]
or maybe something like this?

def <=> other
( bar + baz ) <=> ( other.bar + other.baz )
end

That’s probably not what the OP wants. It would return true for

lorem.bar = 1
lorem.baz = 2

ipsum.bar = 2
ipsum.baz = 1

The <=> operator is useful for objects that have a natural ordering.
This might not make sense here. (What would be the natural, distinct
ordering of songs with a title and an artist? Which attribute
should have higher priority?..)

To test for equality of all attributes, you could simply do

def ==(other)
bar == other.bar && baz == other.baz
end

(This will work fine as long as you only compare objects of the
same class.)

On 18/06/2013 2:06 AM, Josh C. wrote:

  end
  puts lorem == ipsum # false

class Foo
attr_accessor :bar, :baz
def ==(foo)
foo.kind_of?(self.class) && bar == foo.bar && baz == foo.baz
end
end

Only define <=> if your foos are ordered.
I suspect that the OP is looking for something a bit more generalised.

    def ==(rhs)
        return false unless rhs.is_a?(self.class)
        instance_variables.each do |var|
            return false unless instance_variable_get(var) ==

rhs.instance_variable_get(var)
end
return true
end

Note that the first line of this can be changed according to taste:

  • as above, two objects will compare as equal if the class of the right
    hand side is the same as or a sub-class of the left hand side
  • remove it completely in which case you get “duck typing” equality (no
    relationship needed between classes of object)
  • change it to “return false unless rhs.class == lhs.class” in which
    case the two objects must be of the same class
    In the first case, a == b will not necessarily return the same as b ==
    a.

Also, note that this code allows the right hand side to have extra
instance variables that the left hand side does not, but they can still
compare as equal. That may or may not be desirable. If not, you need to
test for it as well. Again, the way I’ve written it, a == b is not the
same as b == a.

Graham

On Mon, Jun 17, 2013 at 4:25 PM, Graham Menhennitt
<[email protected]

wrote:

  attr_accessor :bar, :baz

class Foo
def ==(rhs)
hand side is the same as or a sub-class of the left hand side
as b == a.

Perhaps, but I think this is not a good approach.

It’s too magical:

  • You give up control of what constitutes equality (these kind of
    implicit
    assumptions seem to always break down)
  • You store something in a var and all of a sudden your objects aren’t
    showing up equal. It really sucks when making some change that doesn’t
    matter suddenly causes everything to break for no obvious reason.
  • You can’t look at the behaviour and figure out what it’s doing, b/c
    it’s
    violating encapsulation.

It’s suddenly state based instead of value based:

  • Say you have a method that lazily initializes some var: def users() @users ||= [] end and someone called obj.users.any? on the one, but
    not
    the other. Now your objects aren’t equal.
  • It doesn’t matter if bank1 has @[email protected]=0 and bank2 has
    @[email protected]=1, these have the same value, but it doesn’t matter,
    b/c
    the state representation is different,
  • You can’t pass a mock in, b/c it’s no longer about interfaces, it’s
    now
    about the state of these variables.

-Josh

On Tue, Jun 18, 2013 at 12:11 AM, Josh C. [email protected]
wrote:

I suspect that the OP is looking for something a bit more generalised.

    def ==(rhs)
        return false unless rhs.is_a?(self.class)

That comparison is moot because it is not symmetric (see article linked
below).

  • remove it completely in which case you get “duck typing” equality (no

violating encapsulation.

All very good points! If this was a relational database we were talking
about the primary key. This is also something we define explicitly and
not
accidentally.

In order to not repeat myself too much I reference my blog article about
the topic:
http://blog.rubybestpractices.com/posts/rklemme/018-Complete_Class.html

Kind regards

robert