Is there a Ruby method for comparing two objects based on whether all of their instance variables are equal? The method would behave like this code.
class Coordinates
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
end
coordinates1 = Coordinates.new(0, 0)
coordinates2 = Coordinates.new(0, 0)
coordinates3 = Coordinates.new(1, 0)
compare(coordinates1, coordinates1) # => true
compare(coordinates1, coordinates2) # => true
compare(coordinates1, coordinates3) # => false
Does this method or something similar exist?
There is no built-in method for this, but you could quite easily write one. However, I think you're asking an XY question.
Here is what I think the question is supposed to say:
How should I define a method to check that two
Coordinatesinstances are equal?
And here's my answer:
Define a custom == method:
class Coordinates
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def ==(other)
return super unless other.is_a?(Coordinates)
x == other.x && y == other.y
end
end
...But in the spirit of StackOverflow, here's some meta-programming to check whether all instance variables have the same name and value:
# returns true if all objects have the same instance variable names and values
def compare(*objects)
objects.map do |object|
object.instance_variables.map do |var_name|
[var_name, object.instance_variable_get(var_name)]
end
end.uniq.count == 1
end
Case 1
class A
def initialize(x,y)
@x = x
@y = y
end
def m
@x = 5
@y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x00005d22a3878048 @x=1, @y=2>
a1.m
a1 #=> #<A:0x00005d22a3878048 @x=5, @y=6>
a2 = A.new(3,4)
#=> #<A:0x00005d22a38b5330 @x=3, @y=4>
a2.m
a2 #=> #<A:0x00005d22a38b5330 @x=5, @y=6>
Then,
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true
tells us that the values of @x and the values of @y are the same for both instances.
Case 2
Now let's change the code so that another instance variable is added conditionally.
class A
def initialize(x,y)
@x = x
@y = y
end
def m
@z = 3 if @x == 3
@x = 5
@y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x000057d1fd563c78 @x=1, @y=2>
a1.m
a1 #=> #<A:0x000057d1fd27f200 @x=5, @y=6>
a2 = A.new(3,4)
#=> #<A:0x000057d1fd57cb38 @x=3, @y=4>
a2.m
a2 #=> #<A:0x000057d1fd2f9e10 @x=5, @y=6, @z=3>
At this point are all instance variables of one of these instances equal to the corresponding instance variable of the other instance? No, because a2 has an additional instance variable, @z. Therefore,
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true
gives the wrong answer, for obvious reasons. Perhaps we could test as follows:
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) } &&
a2.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true && false => false
This has a gotcha, however, if @z equals nil.
Case 3
class A
def initialize(x,y)
@x = x
@y = y
end
def m
@z = nil if @x == 3
@x = 5
@y = 6
end
end
a1 = A.new(1,2)
#=> #<A:0x000057d1fd2d18e8 @x=1, @y=2>
a1.m
a1 #=> #<A:0x000057d1fd2d18e8 @x=5, @y=6>
a2 = A.new(3,4)
#=> #<A:0x000057d1fd46b460 @x=3, @y=4>
a2.m
a2
#=> #<A:0x000057d1fd46b460 @x=5, @y=6, @z=nil>
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) } &&
a2.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> true && true => true
We obtain this incorrect result because:
class A
end
A.new.instance_variable_get(:@z)
#=> nil
We therefore must confirm that if one instance has an instance variable named e, so does the other instance, and that each pair of instance variables with the same name are equal. One way to do that is as follows:
(a1.instance_variables.sort == a2.instance_variables.sort) &&
a1.instance_variables.all? { |e|
a1.instance_variable_get(e) == a2.instance_variable_get(e) }
#=> false && true => false
See Enumerable#all?, Object#instance_variables and Object#instance_variable_get.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With