Compare JSON Arrays
Compare two JSON arrays side-by-side. Detect added, removed, and modified elements with deep recursive diff and key-based matching.
About
Comparing JSON arrays by visual inspection fails beyond a few dozen elements. Missed differences in API responses, database exports, or configuration files cause silent data corruption and downstream bugs that surface days later. This tool performs recursive structural comparison of two JSON arrays. It matches elements by detected primary key (id, _id, key) when present, falling back to positional index comparison. Each nested object, array, and primitive is diffed to the leaf level. Results classify every element as added, removed, modified, or unchanged with exact path reporting.
The comparison engine handles type mismatches (NULL vs. string, number vs. boolean), nested depth up to 50 levels, and arrays of mixed types. Note: for arrays without identifiable keys, element reordering is reported as modification rather than movement. Approximate time complexity is O(n ⋅ d) where n is element count and d is average nesting depth.
Formulas
The comparison produces a diff tree. Each node carries a status flag derived from structural analysis:
Where a is the element from Array A and b is the matched element from Array B. Key-based matching resolves b by searching Array B for an object whose primary key value equals that of a. The primary key is auto-detected by scanning the first element of each array for common identifier fields: id, _id, key, uuid, name.
The deep equality function recurses with a maximum depth of 50:
Statistics are computed as a single pass over the diff tree:
Where each S is a count of top-level array elements classified by their diff status.
Reference Data
| Scenario | Detection | Matching Strategy | Example |
|---|---|---|---|
| Element added to Array B | Added | Key or index absent in A | {"id":5} exists only in B |
| Element removed from Array A | Removed | Key or index absent in B | {"id":3} exists only in A |
| Primitive value changed | Modified | Same key/index, different value | "name":"Alice" → "name":"Bob" |
| Type changed | Modified | Same path, different JSON type | "age":25 → "age":"25" |
| Nested object changed | Modified (deep) | Recursive key comparison | "address.city" differs |
| Nested array length differs | Modified + Added/Removed | Index-based sub-diff | "tags":[1,2] → "tags":[1,2,3] |
| Element identical | Unchanged | Deep equality check | All keys and values match recursively |
| Null vs. missing key | Modified | Explicit null ≠ absent key | "x":null vs. key absent |
| Empty object vs. populated | Modified | Key count differs | {} vs. {"a":1} |
| Array of primitives | Per-index | Index-based comparison | [1,2,3] vs. [1,4,3] |
| Mixed types in array | Per-index + type check | Index-based with type annotation | [1,"a",true] vs. [1,"b",false] |
| Large arrays (>1000 elements) | Full diff | Key-match preferred for performance | API response comparison |
| Duplicate keys in objects | Last-value-wins (JSON spec) | Standard JSON.parse behavior | Per RFC 8259 |
| Unicode strings | Exact match | Code-point comparison | "caf\u00e9" vs. "café" |
| Number precision | Strict equality | IEEE 754 double comparison | 0.1+0.2 ≠ 0.3 |