Comparing Objects With Many Attributes
If you’re implementing your own == method for a complex class you sometimes end up with code that looks like this
1 def ==( other ) 2 self.attribute_1 == other.attribute_1 && 3 self.attribute_2 == other.attribute_2 && 4 self.attribute_3 == other.attribute_3 && 5 # Code snipped for brevity 6 self.attribute_19 == other.attribute_19 && 7 self.attribute_20 == other.attribute_20 8 end
If you’re not paying attention you could end up comparing the wrong attributes or comparing two attributes of the same object or even the same attribute with itself. Similarily leaving out a && can cause all code before that point to be ignored. Using some of the built in features of Ruby and it’s core API we can almost eliminate this as well as make the code easier to read and maintain.
Firstly, thanks to Array#==
1 [ self.attribute_1, self.attribute_2 ] == [ other.attribute_1, other.attribute_2 ]
is equivalent to
1 self.attribute_1 == other.attribute_1 && self.attribute_2 == other.attribute_2
If we combine that with the fact that we can return values from a lambda, the first code listing at the top can be rewritten as
1 def ==( other ) 2 attribute_values_of = lambda{ |object| [ object.attribute_1, 3 object.attribute_2, 4 # Code snipped for brevity 5 object.attribute_20 ]} 6 attribute_values_of[ self ] == attribute_values_of[ other ] 7 end
By setting the attribute values that are returned in a lambda that is applied to both objects in the comparison you can ensure that all attributes are correctly compared. Adding new attributes to the comparison entails merely adding them into the returned array.
Farrel Lifson is a lead developer at Aimred.