Checking Object Equality
Tags: javascript, equality, operators, objects
Published: 2020-03-20 17:21:45

When we test for equality among Javascript primitives, like strings and numbers, we have a couple of quick and easy solutions. we can use our equality operators (<i>"==="</i>, <i>"=="</i>, and <i>Object.is</i>) and quickly determine if two variables are equal to each other. When we try and do this with functions and objects these solutions fall apart. We can't simply use the equality operators as is. 

Why is that?
 
We can think of every primitive value we create as just linking to an ever-existing value. What do we mean by this?

```javascript
let deGrom = 48
let rosario = 1
let valentine = 1
```

We can link our variables to primitives. We can imagine all Javascript primitive values permanently exist and we are simply connecting the variable, to the value.

So, if I attempt to do an equality check…


```javascript
console.log(rosario === valentine)
// true
```

We can confirm they are equal! 

Upon creation, objects and functions do not point to permanently existing values like primitives. They always create unique values. 

```javascript
console.log({} === {})
// false
console.log(
   function redSox(){ return 'dirty cheaters' } === 
   function astros(){ return 'dirty cheaters' }
)
// false
```

Here we have created two new objects and two new functions. Because they are unique, they won't be equal to each other. Even if we define objects or functions that hold the same exact properties, they can't be equal to each other. We would have to make sure our variables reference the same object for the value of each variable to be equal to each other.

```javascript
// Objects
let team = {valentine: 1, rosario: 1, deGrom: 48}
let teamLink = c
console.log(Object.is(team, teamLink))
// true


// Functions
let cheaters = function astros(){ return 'dirty cheaters' }
let cheatersLink = cheaters
console.log(Object.is(cheaters, cheatersLink))
// true
```

So how can we compare objects?

We have a couple of options available to us.

<h2>Stringify!</h2>

One way is to use <i>JSON.stringify()</i> to compare values.


```javascript
let somePlayers1 = 
    JSON.stringify({
        valentine: 1, 
        deGrom: 48, 
        rosario: 1
    })

let somePlayers2 = 
    JSON.stringify({
        valentine: 1, 
        deGrom: 48, 
        rosario: 1
    })

console.log(
    somePlayers1 = somePlayers2
)
// true
```

It worked! Let's try a similar example.


```javascript

let somePlayers1 = 
    JSON.stringify({
        valentine: 1, 
        rosario: 1, 
        deGrom: 48
    })

let somePlayers2 = 
    JSON.stringify({
        valentine: 1, 
        deGrom: 48, 
        rosario: 1
    })

console.log(
    somePlayers1 = somePlayers2
)
// false
```

But wait! That didn't work. Why not?

<strong>Order is not guaranteed among objects, so it is important to sort them before attempting a stringify comparison</strong>

<strong>When comparing strings, they must mirror each other exactly.</strong>

One way is to sort our objects keys alphabetically by using <i>Object.keys</i>

```javascript
let somePlayers1 = 
    Object.keys({valentine: 1, rosario: 1, deGrom: 48}).sort()

let somePlayers2 = 
    Object.keys({valentine: 1, deGrom: 48, rosario: 1}).sort()

console.log(
    JSON.stringify(somePlayers1) === JSON.stringify(somePlayers2)
)
// true
```

By using <i>JSON.stringify</i> we've serialized our object into a string, a primitive. Now the two variables <i>somePlayers1</i> and <i>somePlayers2</i> both equal <i>{deGrom: 48, rosario: 1, valentine: 1}</i>. We can now compare these two values using equality operators.

Stringify-ing our object is not the most performant method, but it does work.

What happens when we have deeply nested objects though? We would need to perform the same steps we did above for each level of our object.

```javascript
function sortAllKeys(o){
  if (typeof o !== 'object' || !o) {
    return o
  } else {
    return Object.keys(o)
              .sort()
              .reduce((c, key) => (c[key] = sortAllKeys(o[key])), {})
  }
}
```
Here we are recursively calling our <i>sortAllKeys</i> function. When we finally recurse to the point where our keys point to Javascript primitives and are sorted, we are going to do our type check. 

Our if statement will only return true when our keys stop pointing to nested objects. When <i>o</i> evaluates to being a primitive there is no need to recurse anymore and as we pop calls off the stack we can eventually return our deeply nested, sorted object.

```javascript
let somePlayers1 = {
    valentine: {
        number: 1
    }, 
    rosario: {
       number: 1, 
       isHeGood: true
    },      
    deGrom: { 
        number: 48,
        isHeGood: true
    }
}

let somePlayers2 = {
    valentine: {
        number: 1
    },
    deGrom: { 
        number: 48,
        isHeGood: true
    },
    rosario: {
       isHeGood: true,
       number: 1
    }
}

console.log(
   JSON.stringify(sortAllKeys(deGrom)) === 
   JSON.stringify(sortAllKeys(scherzer))
)
//true
```

We can use a method like this to compare deeply nested objects, but I think this hints at a deeper problem with trying to use just <i>JSON.stringify</i>.


<h2>Outside Library (underscore/lodash)</h2>

Using an outside library is probably the simplest, easiest, and quickest option we have. Prior to ES6, lodash and underscore provided many Array and Object methods that didn't natively exist in Javascript. This solved a lot of problems. Instead of creating new methods, you would be provided with tested, production ready methods. Why create something that already exists? Tasks like cloning objects, array flattening  and object equality (hey that's us!) are as simple as adding the library to your project.

As an example, lodash provides us with an <i>isEqual</i> which according to lodash documentation, "Performs a deep comparison between two values to determine if they are equivalent."

```javascript
import isEqual from 'lodash.isequal'

let deGrom = {
    position: "pitcher", 
    ace: true,     
    organization: {
        name: "MLB", 
        league: "National"
    }
}

let scherzer = {
    position: "pitcher", 
    ace: true, 
    organization: { 
        league: "National",
        name: "MLB"
    }
}

console.log(isEqual(deGrom, scherzer))
// true
```

Although <i>deGrom</i> and <i>scherzer</i>, two aces for their respective teams, are each variables holding objects that look the same, they are different objects and individually created.

Using the <i>isEqual</i> method from lodash, when we compare the two variables we get true. 

As an added bonus once these methods find a key/value pair that is not found on the other comparison object, they'll return false. Stringify has to serialize both objects before determining whether they are equal or not.

<h2>Creating Your Own Method</h1>

This is a fun one if you wanna get a deeper understanding of object equality and how different libraries and programmers try to implement it. We've seen that a combination of recursion, sorting, and checking whether a key/value pair is primitive or object is one route.

Looking at lodash or underscore's implementations a piecing through the code is helpful, and can help solidify understanding just how to implement an algorithm to check object equality. 

I would love to see more ways to compare objects below, and...

<h1><strong><i>Let's Go Mets!</strong></i></h1>




<i>Thank you to Dan Abramov's Just Javascript series for helping to solidify primitive and object comparison concepts</i>

<a href="https://justjavascript.com/">Just Javascript Course</a>