Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vanilla JS Deep Object Comparison #6

Open
corysimmons opened this issue Apr 21, 2020 · 0 comments
Open

Vanilla JS Deep Object Comparison #6

corysimmons opened this issue Apr 21, 2020 · 0 comments

Comments

@corysimmons
Copy link
Owner

corysimmons commented Apr 21, 2020


pubDate: 2019-10-17

image

I very randomly got curious how to compare objects in Javascript so opened DevTools and began tinkering. I remembered you could use JSON.stringify() to do this, and it even works with deep equality. Example: JSON.stringify({a: 1, b: {c: 2}}) === JSON.stringify({a: 1, b: {c: 2}})

But it breaks the second those properties aren't in the same order. For instance, JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1}) fails.

Like most deep things, I figured a recursive function would be necessary, and out of laziness, started Googling around to see if there was a really slick ES6 way to do it (like some fresh/obscure Object.deepEqual native method I hadn't learned yet or something), but alas, there wasn't.

I was certain Lodash had something. And of course it did. And in practice, I would probably use it because it's probably better tested for fringe cases, probably pretty performant, and Lodash is familiar to all developers and comes with lots of usage documentation.

But I was curious how it worked so I dove into Lodash code for a second and it was depending on sooo many little stupid building block modules for every single thing, that reverse engineering it made the problem "not fun".

So, I just figured I'd try to solve it myself (which I'm proud to say I did really quickly—usually these things hamstring me for a couple hours but I solved this one instantly) and submit it to StackOverflow (since its an incredibly popular question, and my solution is elegant, maybe I could get some of that sweet SO karma), but all the threads for it were locked, so HERE WE ARE. 😩

const nestedObj1 = { b: 2, c: { e: 3, d: 4 }, a: 1 }
const nestedObj2 = { a: 1, b: 2, c: { d: 4, e: 3 } }
const nestedObj3 = { a: 1, b: 2, c: { d: 4, e: 9999999999999 } }
const shallowObj1 = { a: 1, b: 2 }
const shallowObj2 = { b: 2, a: 1 }
const shallowObj3 = { a: 1, b: 9999999999999 }

// Recursively sorts the keys and returns a fresh object
function sortObj(obj) {
  return Object
    .keys(obj)
    .sort()
    .reduce((acc, curr) => {
      if (typeof obj[curr] === 'object') {
        acc[curr] = sortObj(obj[curr])
      } else {
        acc[curr] = obj[curr]
      }

      return acc
    }, {})
}

// Does that stringify comparison stuff to the _now 100% recursively sorted_ objects
function deepEqual(obj1, obj2) {
  return JSON.stringify(sortObj(obj1)) === JSON.stringify(sortObj(obj2))
}

deepEqual(nestedObj1, nestedObj2) // true
deepEqual(nestedObj1, nestedObj3) // false
deepEqual(shallowObj1, shallowObj2) // true
deepEqual(shallowObj1, shallowObj3) // false

That's it. Curious how it performs vs Lodash (maybe my next post should be figuring out how to do isolated function benchmarks 🤔), but it seems to work well in these few generic tests.

Update: As it turns out my implementation is about 35% slower than Lodash's. 🤷‍♂️ Ah well, it was fun to tinker with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant