Software Development

JavaScript — How equal are 2 values?

May 14, 2014
#development

Have you ever wondered whether JavaScript has a native function for checking whether 2 values are deeply equal?

Introduction

Ok, well maybe you haven’t but in case you were wondering, unfortunately for us this function doesn’t exist natively in JavaScript. But luckily for us, even for the slightly initiated JavaScript ninja, it’s quite possible to write this function ourselves — and this post will demonstrate one such implementation.

But before beginning, it makes sense to take a brief detour to explain why this problem is interesting. In JavaScript, there are 2 types of values:

Primitive types

  • Strings
  • Numbers
  • Booleans
  • Null
  • Undefined

Reference types

  • Everything else, including Objects and Arrays


Primitive types

For primitive types, determining whether 2 values are equal is actually pretty simple — JavaScript provides a native “===” (triple equals) operator which tests for exactly this! Here’s some examples to get the point across:

var name1 = “Benji”;
var name2 = “Ben”;
var name3 = “Benji”;
console.log( name1 === name2 ); // returns false
console.log( name1 === name3 ); // returns true
var nothing = null;
var notDefined = undefined;
var alsoNothing = null;
console.log( nothing === notDefined ); //returns false
console.log( nothing === alsoNothing ); // returns true

In both of these examples, we see that the “===” operator correctly identifies for us whether two values are equal to each other. Woohoo, good job JavaScript!

Reference types

Now, what about for reference types? Again, let’s see some examples and hopefully this will explain how equivalence for JavaScript reference types is a little more complicated …

var disneyCharacter1 = {
 name: “Nemo”,
 movie: “Finding Nemo”
};

var disneyCharacter2 = {
 name: “Simba”,
 movie: “Lion King”
};

var disneyCharacter3 = {
 name: “Nemo”,
 movie: “Finding Nemo”
};

// This one is simple — obviously Nemo isn’t the same as Simba!
// So we’d expect this to return false.
console.log( disneyCharacter1 === disneyCharacter2 );

// But what about here?? We’d expect Nemo to be, well, Nemo right?
// Unfortunately, this returns false in JavaScript!
console.log( disneyCharacter1 === disneyCharacter3 );


The problem

So why does this last example return false in JavaScript? Without getting into the nitty gritty details, it’s enough to say that testing whether 2 reference values in JavaScript are triple equals to each other will return false unless they are identically equal to each other. In laymen’s terms, what this means is that 2 reference values will be triple equals to each other if and only if they are referring to the same exact value!

// returns false because these 2 values aren’t referring to identical values in memory
console.log( disneyCharacter1 === disneyCharacter3 );

// returns true because these 2 values are actually the same identical value
console.log( disneyCharacter1 === disneyCharacter1 );

Nemo

Another way to think of this is to imagine that there are TWO copies of Nemo; even though they share the same name and appear in the same movie, there are actually 2 of them and that’s why they aren’t identically equal to each other. But when you ask one particular Nemo whether it’s identically equal to itself, then of course it’ll respond with yes!

The solution

In our previous example, wouldn’t it be nice if both disneyCharacter1 and disneyCharacter3 responded with “true” when asked if they’re equal to each other? I mean, we all know that they should be equal to each other since they have the same values for each of their properties. That’s where the concept of deep equals comes into play. We can think of 2 values as being deeply equal to each other if for every property in both objects, they share the same values for those properties. Well, let’s go ahead and implement a JavaScript function to check whether 2 values are deeply equal to each other!

var deepEquals = function ( first, second ) {

 /*
  * First, let’s check whether the 2 values are both primitive types
  * If they are both primitive types, then we can just use the “===” operator
  * to determine whether they are deeply equal to each other.
  * The isPrimitive function is a helper function I defined below to help us out.
  */
 if ( isPrimitive(first) && isPrimitive(second) ) {
   // If this part of the code is reached, it means that both values are primitive types.
   // In that case, we can simply use our “===” operator to test for deep equality.
   return first === second;
 }

 /*
  * Second, if we reach this part of the code, it means that either:
  * 1) One of the values is a primitive type while the other one isn’t, or
  * 2) Both values are reference types
  * The else—if code directly below will test for the (1) scenario
  */
 else if ( isPrimitive(first) || isPrimitive(second) ) {
   // If the 2 values aren’t even the same types, then we can just return false.
   return false;
 }

 /*
  * Finally, if we reach this part of the code, then we know that we’re in the
  * (2) scenario mentioned above where we are dealing with 2 reference values.
  */
 else {

   // Since we know that both values are reference types,
   // we can get the keys for each of the objects and compare their keys array.
   var firstKeys = Object.keys( first );
   var secondKeys = Object.keys( second );

   // Given these 2 keys arrays, we know that if they don’t even have the
   // same lengths, then they must not be deeply equal.
   if ( firstKeys.length !== secondKeys.length ) {
     return false;
   }

   // At this point, we know that these 2 values have keys arrays that have the same length
   // We now want to check whether for EACH of the keys, both objects have the same corresponding values.
   else {
     for ( var prop in first ) {

       // We know both keys arrays have the same length,
       // but do they have the same keys in both arrays? This test checks for that.
       if ( second[prop] === undefined ) {
         // If we can’t the same key in both objects,
         // then we know the two objects are not deeply equal.
         return false;
       }

       // At this point, we see that both objects have the same particular
       // key but are there corresponding values deeply equal?
       // Well, would you look at that — this is a recursive problem!
       // If we determine that for this particular key, the 2 object’s corresponding
       // values are not deeply equal, then we know that the 2 object’s are also not deeply equal.
       else if (!deepEquals(first[prop], second[prop])) {
         return false;
       }
     }

     // If the code reaches this point, then we know we’ve gone through all the key / value pairs
     // in both objects and all their key / value pairs are deeply equal — we can then return true
     return true;
   }
 }
};

// Helper function to determine whether a JavaScript value is a primitive type
var isPrimitive = function ( value ) {
 return (value === void 0 || value === null || typeof value === “number” || typeof val

Woohoo — we made it! At this point, you should try out the code above and confirm for yourself that this is indeed a particular implementation of deep equals. In fact, let’s try it with our Disney example!

var disneyCharacter1 = {
 name: “Nemo”,
 movie: “Finding Nemo”
};

var disneyCharacter3 = {
 name: “Nemo”,
 movie: “Finding Nemo”
};

// This does indeed return true!
console.log(deepEquals(disneyCharacter1, disneyCharacter3));

// returns true — cool, the function works for primitive values too!
console.log(deepEquals(518, 518));

The end

So there you go — we’ve gone ahead and implemented a JavaScript function to compare any 2 types of values and determine whether or not they’re deeply equal. It’s important to remember that this is different from having 2 values which are identically equal to each other — that’s what the “===” operator is for!


You May Also Like