DEV Community

Cover image for Equality of Data Structures: Ruby vs. JavaScript
Anna Rankin
Anna Rankin

Posted on • Updated on

Equality of Data Structures: Ruby vs. JavaScript

Skip to the TL;DR

In recent weeks I've been reading through JavaScript Allongé by Reginald Braithwaite (an excellent look at the fundamentals of JS through a functional programming lens) with a coworker. My coworker brought up something that didn't make sense to her:

In JavaScript, why isn't an empty array equal to an empty array? What's the difference between the two identical objects?

To someone who's been working with JavaScript for a while, the answer might seem straightforward: "They're two different arrays; of course one is not equal to the other!" A Rubyist like my friend, however, may see it differently: "These two arrays have the same content—what do you mean they're not equal?" As I see it, this is a philosophical difference between the meanings of the comparison operators in Ruby and JavaScript. One language uses the concept of object equivalence to compare data structures, while the other checks explicitly for object identity.

Note: I'm not going to discuss the JS double equal or Ruby's threequal operators, as these are more than just simple comparison operators (I also avoid using them because they can be confusing and misleading!).

Pixel art version of the Ruby programming language's logo

Equality of Data Structures in Ruby

When you compare data structures using the built-in behavior of == in Ruby, you're really comparing the contents of the object—and in the case of an array, you're also checking that the order of the elements is the same in both. This means that two variables that point to different objects in memory could be equal.

> first_array, second_array = [1,2,3], [1,2,3]

> first_array == second_array
=> true
Enter fullscreen mode Exit fullscreen mode

If for some reason you truly want to check and see that two variables refer to the same object, you can check object's ID—or preferably, use the .equal? method:

> first_array, second_array = [1,2,3], [1,2,3]

> first_array.object_id == second_array.object_id # 70176467875700 == 70176467875680
=> false
> first_array.equal?(second_array) # a clearer way to perform this comparison
=> false
Enter fullscreen mode Exit fullscreen mode

Unlike in JavaScript, the comparison operator in Ruby is actually a method defined on the class you're comparing (awesome walkthrough of this concept here). Since == is just a method, you can even override it if you like! This is a reasonable practice if you're writing custom classes that need to be compared to each other. Silly example below:

> class Ditto
*   def self.==(other)
*     true
*   end
* end

> Ditto == 'Pikachu'
=> true
> Ditto == 2
=> true
> Ditto == false
=> true
Enter fullscreen mode Exit fullscreen mode

Pixel art version of the JavaScript Logo

Equality of Data Structures in JavaScript

Unlike Ruby, JavaScript does not expose a unique ID for objects because it doesn't need to. Data structures are compared by identity by default. If two variables are equal, you can be sure that they point to the same object in memory.

> const firstArray = [1,2,3]
> const secondArray = [1,2,3]
> firstArray === secondArray
false
Enter fullscreen mode Exit fullscreen mode

If you want to check and see if two separate data structures have the same content, you'll have to write your own logic to check—or use a function from a library like Lodash.

// Super naïve implementation:
const arraysAreEqual = (array1, array2) => {
  return array1.every((el, index) => el === array2[index])
}
> arraysAreEqual([1,2,3],[1,2,3])
true
> arraysAreEqual([1,2,3],['a','b','c'])
false
Enter fullscreen mode Exit fullscreen mode

TL;DR

JavaScript's === checks to see if the two variables it's comparing point to the same data structure, while Ruby's == method checks to see if the contents of two arrays or hashes are equivalent.

RB vs. JS Equality - first image of two closed boxes & JavaScript logo, text "Is this the same box?" Second image of two open boxes with the numbers 1,2, and 3 in each, text "Is the same stuff in this box?"

Ruby's [1,2,3] == [1,2,3] translates to [1,2,3].every((el, index) => el === [1,2,3][index]) in JS.

JavaScript's [1,2,3] === [1,2,3] translates to [1,2,3].equal?([1,2,3]) in Ruby.

References

Hopefully this helps you wrap your mind around what the two different languages expect when comparing data structures! If you're interested in going deeper, I've put together some references:

Top comments (14)

Collapse
 
nickytonline profile image
Nick Taylor

Same box vs same stuff in the boxes. Great analogy.

Collapse
 
annarankin profile image
Anna Rankin

Thank you Nick! 😁

Collapse
 
ahmaddeel profile image
AhmadDeel • Edited

Great post but two notes:
First => on my personal experience js checks if two objects are the same; I mean if they have the same reference. Like:
Const b = {
value:1
};
Const a = b;
a === b // true

I didn't test that for arrays. And you mentioned that js checks to see if the two thing are equal (in value ? ) sounds to me like js is checking their references. Am I right ?

Second => I think the place of TL;DR should be at the top of this post.
Happy coding.

Collapse
 
annarankin profile image
Anna Rankin

Hi Ahmad - this is correct! For data structures, it does check to see if the two things being compared point to the same reference in memory - for primitives, it compares the items by value. I'd originally had a bit in the article about reference vs identity, but I chopped it out because it was getting convoluted 😳And I think you're right, the TL;DR would be better suited at the beginning. Thanks for your input 🙌

Collapse
 
tsupinie profile image
Tim Supinie

I guess isn't Javascript's == operator supposed to be an "is this the same stuff in the box" check? There's all this implicit typecasting going on, but at its heart, it's an equality check designed to determine if two variables contain the same value. So I'm not sure you can draw the conclusion of philosophical differences in language design when Javascript has an operator with the same intent as in Ruby.

Collapse
 
annarankin profile image
Anna Rankin

Hi Tim, good point! However, if you try out [1,2,3] == [1,2,3] in your console, you'll see it returns false. JS's double-equals will coerce types, but it won't perform a recursive equality check the way it does in Ruby. 😵

Collapse
 
joelnet profile image
JavaScript Joel • Edited

Loving these graphics! Do you make them yourself for these articles?

Collapse
 
annarankin profile image
Anna Rankin

Thank you very much, Joel! 😊 Yes, I do - any chance I get to draw nerdy pixel art, I jump on lol

Collapse
 
joelnet profile image
JavaScript Joel

Please never stop! 😊

Collapse
 
deusmxsabrina profile image
Sabrina

Thanks for the post, as a current Rubyist who is always curious about learning more JS this would have been a total head scratcher for me!

Collapse
 
tammalee profile image
Tammy Lee

Thank you very much for writing this up, Anna. It was very succinct and I loved your illustrations. XD (And the Ditto class. Lol!)

Collapse
 
annarankin profile image
Anna Rankin

Thank you Tammy! I'm glad you liked it 😁

Collapse
 
nahuef profile image
Nahuel

You wrote the whole post just to show off your amazing pixel art skills right?!

Loved them.

Collapse
 
annarankin profile image
Anna Rankin

😁😁😁 made my day! Thanks Nahuel!