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.

···

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

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

···

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

I see Joel Pearson.

Why <=> instead of ==?

Thanks Joel Pearson.

···

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

Because that method gives you these all in one:

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

···

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

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.

···

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

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 the Ruby 2.0 documentation for it.
http://ruby-doc.org/core-2.0/Comparable.html

···

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

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.

···

On Mon, Jun 17, 2013 at 10:52 AM, Thom T. <lists@ruby-forum.com> wrote:

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.

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:

···

--
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 6:02 PM, Thom T. <lists@ruby-forum.com> wrote:

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

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

Josh Cheek 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:

···

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

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

···

Am 17.06.2013 17:56, schrieb Joel Pearson:

--
<https://github.com/stomar/&gt;

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 18/06/2013 2:06 AM, Josh Cheek wrote:

On Mon, Jun 17, 2013 at 10:52 AM, Thom T. <lists@ruby-forum.com > <mailto:lists@ruby-forum.com>> wrote:

    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.

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.

-

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 @pennies=100@dollars=0 and bank2 has
@pennies=0@dollars=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 Mon, Jun 17, 2013 at 4:25 PM, Graham Menhennitt <graham@menhennitt.com.au > wrote:

On 18/06/2013 2:06 AM, Josh Cheek wrote:

On Mon, Jun 17, 2013 at 10:52 AM, Thom T. <lists@ruby-forum.com> wrote:

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.

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.

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.

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

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

            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.

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.

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

···

On Tue, Jun 18, 2013 at 12:11 AM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Mon, Jun 17, 2013 at 4:25 PM, Graham Menhennitt < > graham@menhennitt.com.au> wrote:

On 18/06/2013 2:06 AM, Josh Cheek wrote:
On Mon, Jun 17, 2013 at 10:52 AM, Thom T. <lists@ruby-forum.com> wrote:

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