JSON tools
Compare Arrays of Objects in JavaScript: Order and Identity
Choose positional or identifier-based comparison, compare JSON values safely, and test missing keys, reordering and duplicate IDs.
Choose the meaning of equal before writing a comparison
Two arrays can hold the same records in different orders. A positional comparison treats that reordering as a difference, while a comparison keyed by a unique identifier may treat the records as unchanged. Neither rule is universally correct: a task queue has meaningful order, while a directory returned in unspecified server order may need identifier-based comparison.
Reference equality does not compare record contents. Two separately parsed arrays are different JavaScript objects even when their JSON values match. Conversely, two variables pointing to the same mutable array remain reference-equal after its contents change. Select the comparison rule based on the data contract rather than whichever operator produces the expected result for one example.
Compare finite JSON data by value
The function below compares ordinary JSON-compatible values by position for arrays and by own property names for objects. Its inputs must already be parsed, acyclic JSON data with finite, representable numbers. It is not a general comparator for Dates, Maps, functions, circular application objects or arbitrary class instances.
function equalJSON(a, b) {
if (a === b) return true;
if (a === null || b === null ||
typeof a !== "object" || typeof b !== "object") return false;
if (Array.isArray(a) !== Array.isArray(b)) return false;
if (Array.isArray(a)) {
return a.length === b.length &&
a.every((value, index) => equalJSON(value, b[index]));
}
const keys = Object.keys(a);
return keys.length === Object.keys(b).length &&
keys.every(key => Object.hasOwn(b, key) && equalJSON(a[key], b[key]));
}
Own-property checks matter for names such as constructor and toString, which may otherwise be found through an object's prototype. The Object.hasOwn reference describes this distinction. A missing property is different from an explicitly present property holding null, and object key order is deliberately ignored by this function.
Handle identifier-based matching as a separate step
If record order is irrelevant, build an index using the agreed identifier and then compare the records with matching identifiers. A Map can hold that index without treating record identifiers as ordinary object property names. Validate that each identifier is present and unique before indexing.
A duplicate identifier is an ambiguity to resolve, not an invitation to silently overwrite the earlier record. For example, two records both labeled item-4 but containing different quantities cannot be represented faithfully by a one-value-per-ID map. Reject the input or use a contract that explicitly supports multiple records per identifier.
Keep the original arrays unchanged while preparing a comparison. Sorting them in place can alter the application state being investigated. Also decide which fields count: a changing fetchedAt timestamp may be operational metadata, while a changed quantity can be the substantive difference the user needs to see.
Test differences that a quick serialization check can miss
Test equal objects with different key insertion order, reversed arrays, one changed nested quantity, a missing property versus null and an empty array versus an empty object. Include an own key named __proto__ created by parsing JSON, since it must remain ordinary input data for this comparison.
Keep exact large identifiers as strings before parsing. If two distinct numeric tokens have already rounded to the same JavaScript number, this function cannot recover the distinction from those values. Choose a lossless representation at the input boundary when exact numeric text matters.
Check duplicate object member names before relying on parsed data as a faithful record of the input. Ordinary parsing can retain only the later value for a repeated name. A comparison of the resulting objects cannot explain a discarded earlier value that is no longer present.
The local Compare JSON Objects Online tool is useful for inspecting JSON differences by path. Array positions remain part of that comparison; it does not infer your application's record identity or silently decide that order is irrelevant. Use a small representative pair and check the reported paths against the rule you selected.


