11

Consider this:

class Aaa
  attr_accessor :a, :b
end

x = Aaa.new
x.a, x.b = 1,2
y = Aaa.new
y.a, y.b = 1,2

puts x == y #=>false

Is there some way to check if all public attributes are equal in classes of same type?

1

4 Answers 4

10
class Aaa
  attr_accessor :a, :b

  def ==(other)
    return self.a == other.a && self.b == other.b
  end
end

x = Aaa.new
x.a,x.b = 1,2
y = Aaa.new
y.a,y.b = 1,2
y = Aaa.new
y.a,y.b = 1,2
z = Aaa.new
z.a,z.b = 1,3

x == y # => true
x == z # => false
Sign up to request clarification or add additional context in comments.

3 Comments

But what if i want to do this using other classes? Or if there are 100 attributes?
In your ==(other) method, you could do self.instance_variables.each do |ivar| self.ivar == other.ivar end You may want to look at === as well.
Using Struct or OpenStruct has these sorts of cases built into them.
9
Aaa = Struct.new(:a, :b)

x = Aaa.new
x.a, x.b = 1,2
y = Aaa.new
y.a, y.b = 1,2

x == y #=> true

Struct defines ==, eql?, and hash for you, so that two Aaas are equal, if their values for a and b are equal. It also defines initialize so that you can optionally pass in the values for a and b when creating the object (Aaa.new(value_for_a, value_for_b)). And it defines to_a to return [a,b].

You can also use Struct.new with a block to define additional methods, so you have the full power of a "normal" class:

Aaa = Struct.new(:a, :b) do
  def c
    a+b
  end
end
Aaa.new(23,42).c #=> 65

2 Comments

This seems fine but I need this functionality for objects, anyway nice response I didn't know about Structs
@dfens: Those are objects. Struct.new is just a factory method for classes. You'd get the exact same behavior if you'd define Aaa using the class keyword and then defined == etc. yourself.
2

We can easily generalize to any number of instances and drop the requirement for getters for the instance variables:

class Aaa
  def initialize(a,b,c)
    @a, @b, @c = a, b, c
  end
end

x = Aaa.new(1,2,3)
y = Aaa.new(1,2,3)
z = Aaa.new(1,2,3)
arr = [x,y,z]

x.instance_variables.map do |v|
  arr.map { |i| i.send(:instance_variable_get,v) }.uniq.size == 1
end.all?
  #=>true

Change z to:

z = Aaa.new(1,2,4)

then:

x.instance_variables.map do |v|
  arr.map { |i| i.send(:instance_variable_get,v) }.uniq.size == 1
end.all?
  #=> false

Comments

0

Extra method to deal with comparing objects in Ruby consists in using hash. For performance reason, when the class are huge, the best is to use hash to compare ruby objects as so :

class Aaa
  attr_accessor :a, :b

  def intialize(value_a,value_b)
     @a = value_a
     @b = value_b
  end 

  def hash(target)
     @a.hash == target.a.hash  && @b.hash == target.b.hash 
  end
end

A = new Aaa('whatever','whenever')
B = new Aaa('however','whoever')
A.hash(B)

1 Comment

This won't work as expected. Two different unequal objects can have the same hash value and the same object may not have the same hash value between invocations of Ruby. Furthermore, #hash is supposed to return an integer, not a boolean. This is only useful to do a quick check to see if two values might be equal, and for bucketing keys in a Hash for quick lookup. ruby-doc.org/3.2.1/Object.html#method-i-hash

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.