# JSON.equals(x, y)

**URL:** <https://es.discourse.group/t/json-equals-x-y/2019>\
**Category:** 💡 Ideas\
**Created:** [April 18, 2024, 8:10am UTC](https://es.discourse.group/t/json-equals-x-y/2019 "2024-04-18T08:10:25Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 18, 2024, 8:10am UTC](https://es.discourse.group/t/json-equals-x-y/2019/1 "2024-04-18T08:10:25Z")

</div>

Polyfill:

```javascript
JSON.equals ??= (x, y) => JSON.stringify(x) === JSON.stringify(y);

JSON.equals([1, "a"], [1, JSON.rawJSON('"a"')]) // true

```

Does what it says on the tin. "Would the JSON representation of these two values match?" Can be optimized to avoid allocating a potentially large string buffer.

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [April 18, 2024, 6:13pm UTC](https://es.discourse.group/t/json-equals-x-y/2019/2 "2024-04-18T18:13:56Z")

</div>

What is the problem this is solving? Solutions generally don't come until stage 1-2.

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 18, 2024, 6:53pm UTC](https://es.discourse.group/t/json-equals-x-y/2019/3 "2024-04-18T18:53:17Z")

</div>

Specifically, the problem this is solving is the common usage of

```javascript
if (JSON.stringify(x) === JSON.stringify(y))

```

which makes me grit my teeth every time I see it, especially on collections of unbounded size. (Perhaps the problem being solved is my dental health.)

There is no standard deep-equality function in standard ECMAScript. The _right_ way to solve the deep-equality problem is probably to write object- and class-specific equality tests, but in the absence of that, people can and do just stringify both objects and see if the string matches.

I understand some of the difficulting in specifying a standard deep-equality function. What exactly does "equality" mean? Which members get reference-checked, and which get value-checked? Does a difference in private fields break equality? Can an object override its native equality behavior? Are object cycles allowed? All extremely important questions that absolutely need to get answered before we can have something like an `Object.equals`.

And in the meantime, people will continue to write the following, found in a commit from 2022:

```javascript
// Why is this something i have to write
equals(thing1, thing2) {
  return JSON.stringify(thing1) === JSON.stringify(thing2);
}

```

And, despite the steadily-decreasing tread depth of my teeth, I can't argue that there's one major benefit from comparing things this way: if people have written `toJSON()` methods on their classes, then it probably means that those methods will return all the _significant_ data, i.e. all the data that probably wants to be compared if you're trying to tell if something has changed.

I would never suggest that a default of some hypothetical generic `Object.equals()` should respect `toJSON()` methods - I'd find that behavior very surprising, in a bad way. But, in cases where we _do_ want to compare "the exported data of these two objects", like, say, if you're trying to decide whether you need to autosave a game, or whether your cache needs refreshing, then a `JSON.equals()` method would be exactly what I want. "Is the hypothetical JSON output of this object equal to the hypothetical JSON output of this other one? No, please don't allocate two 10MB string buffers, all I want is the yes/no." It might also want a version that compares an object against a JSON string, for when you've kept your previous save-export around.

Sorry for the brevity of my original post! I have difficulty knowing when I need to elaborate vs when I need to be succinct, and I erred too far on the latter side this time 😅

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [April 18, 2024, 10:51pm UTC](https://es.discourse.group/t/json-equals-x-y/2019/4 "2024-04-18T22:51:42Z")

</div>

We have [https://github.com/tc39/proposal-array-equality](https://github.com/tc39/proposal-array-equality) and the closer match, the withdrawn [https://github.com/sebmarkbage/ecmascript-shallow-equal](https://github.com/sebmarkbage/ecmascript-shallow-equal) - it's worth reading the notes for the latter proposal in particular.

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 18, 2024, 11:34pm UTC](https://es.discourse.group/t/json-equals-x-y/2019/5 "2024-04-18T23:34:30Z")

</div>

Cool, I'll take a look at those, thanks! Doesn't seem like it'll address the JSON.stringify-equality pattern, but I suppose I'll just have to hope that engines know enough to special-case that at the AST level

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 19, 2024, 12:11am UTC](https://es.discourse.group/t/json-equals-x-y/2019/6 "2024-04-19T00:11:24Z")

</div>

I suppose another solution to the problem of "the only way to get a comparable value out of a deep structure forces you to allocate unbounded amounts of memory" would be something like `JSON.checksum(object)`, which would give you the equivalent of running some sort of digest on whatever the result of `JSON.stringify()` would be, and then you could compare those values.

Given that ECMAScript doesn't really do checksums, though (there's no way to checksum a string, so how would you compare against a static JSON string?), that feels like it would be an extremely unusual solution in this case.

I suppose it's too much to hope for a `structuredEqual` function, huh? `structuredClone()` addressed the other case where people were abusing JSON round-trips, namely

```js
JSON.parse(JSON.stringify(value))

```

But at least in that context, you're _trying_ to allocate a new object. Sure, it might require a round-trip to a 10MB JSON string, but you're left with a 5MB object afterward. In the comparison case, you're not actually trying to allocate anything, you're just getting a true/false out of it.

None of this is hypothetical, by the way. The idle game I've taken over maintenance of has a nasty habit of bogging down unpredictably, and the bug reports I've gotten from people whose computers froze up after they were left running overnight sound a lot more like memory pressure than anything else. I'm not saying that the stringify-equality issue is the sole culprit here (far from it tbh, I'm working on the game's memory footprint but it's slow progress) but it's one I can't easily avoid unless I'm willing to re-implement the entirety of `JSON.stringify` in userland just so I can change the final _emit_ to a _compare_.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [April 19, 2024, 12:28am UTC](https://es.discourse.group/t/json-equals-x-y/2019/7 "2024-04-19T00:28:22Z")

</div>

I'll mention that

```javascript
function equals(thing1, thing2) {
  return JSON.stringify(thing1) === JSON.stringify(thing2);
}

```

Isn't very reliable - it'll also compare the order of object keys, which you wouldn't want to have happen in a comparison algorithm.

---

<div class="post-metadata">

**Author:** ![dmchurch](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/dmchurch/32/1962_2.png) [@dmchurch](https://es.discourse.group/u/dmchurch)\
**Post date:** [April 19, 2024, 12:50am UTC](https://es.discourse.group/t/json-equals-x-y/2019/8 "2024-04-19T00:50:44Z")

</div>

> [@theScottyJam](#):
>
> it'll also compare the order of object keys, which you wouldn't want to have happen in a comparison algorithm.

Unless and until you _do_ want to compare the order of object keys, of course. 😂 That goes toward my point of "it's hard to define what a deep equality function should mean, exactly", and that's why I suggested `JSON.equal` - for when you're literally trying to detect a change in the JSON representation of an object. Like, if I'm trying to decide whether I should save this object off to LocalStorage (not this exact use case, the 10MB JSON doesn't _fit_ in `localStorage` - this is an in-memory cache) then "has my serialized representation changed" is _exactly_ the question I want answered.

But that's not even at issue in my case, because here the code is detecting changes in a very large, very deep, but fixed-structure dataset. The keys aren't at risk of changing, all that matters in this case is whether any of the values have changed.

The reason I haven't replaced the `equals` function in the code is that I _can't_. I've tried iterating the objects by hand, but given that there are a lot of map-objects (not Map objects but the old kind, and not even the fancy null-prototype ones, either) it has to use some code paths that just _crawl_ on some engines.

`JSON.stringify`, on the other hand, walks an entire object tree in native engine code, which makes it significantly faster, in this case, than any other solution. The only other native function I can think of off the top of my head that will walk an entire object tree for you is `structuredClone`, and that's not precisely useful for this case.

So, sadly, the `equals` function has to stay until I can refactor out this entire section of the codebase and remove the comparison entirely, and until I do, I have to leave this ticking memory bomb of an equality function running on all my players' computers. I do admit it does make me feel some kinship with the original author - I've been tempted to add my own comment reading "Why is this something I have to leave in place" 😅

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [April 19, 2024, 3:41am UTC](https://es.discourse.group/t/json-equals-x-y/2019/9 "2024-04-19T03:41:19Z")

</div>

This discussion inspired me to create this post - [Native deep-clone algorithm?](https://es.discourse.group/t/native-deep-clone-algorithm/2020) - I don't directly talk about deep equality in there - I was originally going to, but realized I was already getting too long-winded, so decided not to go there.

But I do agree that the language ought to have some sort of deep comparison algorithm provided, as it is a fairly common task, and it takes a fair amount of boilerplate to do by hand.
