The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A JSON array diff is only as meaningful as the rule that decides which elements in the old and new arrays correspond. JSON syntax records order, but it does not say whether that order matters or whether each element is a record with a lasting identity. When a diff compares elements only by position, it can be structurally correct and still report changes that no person would describe as changes. Reordering a list of users, inserting one item at the top of a list of sections, or rebuilding an array from separately parsed documents can each produce a large, noisy patch for what is really one small edit.
An array can mean two different things
Consider two JSON arrays that hold the same records in a different sequence. In one application, the order is the data: a playlist, a set of ordered steps, or a ranked list where position one really means something. In another, the array is just a container for a collection of entities, such as customers, product variants, or document sections. In that case each object has an identity that survives reordering, and the order in which the objects happen to be stored is incidental.
JSON cannot tell these cases apart. Both look like [{...}, {...}]. The matching rule that a diff uses is therefore part of the design of the diff, not something the parser can supply. Choosing that rule is where most of the difficulty lies.
How JSON Patch addresses array elements
The IETF standard for JSON Patch, RFC 6902, JavaScript Object Notation (JSON) Patch (published as a Standards Track specification in April 2013, by Paul C. Bryan and Mark Nottingham among others), defines a patch as an array of operation objects. It specifies six operations: add, remove, replace, move, copy, and test. Each operation targets a location in the document using a JSON Pointer, and an array element is addressed by its index in the array at the moment the operation runs.
#1 Best Overall
The standard is explicit about sequencing. In the document-structure section it states: “Operations are applied sequentially in the order they appear in the array.” That single sentence has a practical consequence. Each operation sees the array as left by the operations before it, so an index written later in the patch is not an index into the original document.
Insertions and removals shift the indexes that follow
For an array add, the index may not exceed the current array length, and the elements at or above that index move one position to the right. A path ending in - appends. For remove, the elements after the removed one move one position to the left. A move is defined as a removal at from followed by an addition at path.
Take the array ["A", "B", "C", "D"] and a target of ["A", "C"]. A patch that removes /1 first produces ["A", "C", "D"]. If the next operation then removes /3, it fails, because the array now has only three elements. Removing /3 first and then /1 gives the correct result:
[
{ "op": "remove", "path": "/3" },
{ "op": "remove", "path": "/1" }
]
Generating operations from highest index to lowest is one common way to keep earlier removals from invalidating later paths. The principle matters more than the particular ordering: a generator must track the array as it changes, not as it was when the diff started.
The test operation compares values, not identities
RFC 6902’s test operation uses logical JSON equality. Two arrays are equal only if they contain the same number of values and corresponding positions are equal, and the order in which object members are serialized is not significant. This is a rule about values. It does not establish that an object at position 2 in the old array is the same real-world record as an object at position 3 in the new array, even if the two objects have identical fields.
Why an index-only diff produces noise
A diff that matches array elements by position is faithful to the arrays as stored, and it is simple to implement. The cost appears when content is inserted or removed near the start. Everything after that point shifts, and a positional comparison pairs each element with a neighbor it has nothing to do with.
Here is a small example. The old document has three sections, and a new section named “Overview” is added at the top:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchOld: [{"id":"a","title":"Intro"}, {"id":"b","title":"Setup"}, {"id":"c","title":"Usage"}]
New: [{"id":"x","title":"Overview"}, {"id":"a","title":"Intro"}, {"id":"b","title":"Setup"}, {"id":"c","title":"Usage"}]
Matching by index reports that position 0 changed from “Intro” to “Overview”, position 1 changed from “Setup” to “Intro”, and position 2 changed from “Usage” to “Setup”, plus an addition at position 3 for “Usage”. That is three replacements and one addition. The logical change is a single insertion at the front. A reviewer reading the first version of the patch would reasonably conclude that every section was rewritten.
Rank #3
The corrected patch needs one operation:
[
{ "op": "add", "path": "/0", "value": { "id": "x", "title": "Overview" } }
]
Producing that patch requires the diff to know that "a", "b", and "c" are the same entities in both arrays. Position alone cannot supply that knowledge.
Matching strategies and what each one assumes
Most array diff implementations choose among a small number of matching strategies. The longest common subsequence (LCS) algorithm is a common way to align two sequences: it finds the largest set of elements that appear in the same relative order in both arrays and treats the rest as insertions or deletions. LCS is only as good as the equality test it is given. The test determines what counts as a match, and that decision drives everything else.
Strict value and reference equality
The jsondiffpatch library documents an array diff based on LCS whose default matching uses JavaScript strict equality. That works well for primitive values such as strings and numbers, and for objects that are literally the same instance in memory. It does not work for records parsed separately from two JSON documents. Two parsed objects with identical fields are different instances, so they do not match. The library’s documented fallback, when no value or reference matches are found, is positional matching, which brings back the index-based noise described above. An insertion near the start can therefore make the following entries appear modified.
Free tools Windows power users keep installed
One-click scans. No signup required.
Stable key matching
A domain-specific key is the usual way to make record-like objects match across reorderings. The jsondiffpatch documentation describes an objectHash option that returns an identity for each object, with examples that use fields named name, id, and _id, and array index as the fallback when none of them is present. Treat those names as illustrations of the mechanism, not as a recommendation. A field called name is usually not unique and is often editable, which makes it a poor identity.
Choose a key from the schema and the business rules, and check it against the following before relying on it:
- Presence: the key exists on every element that can appear in the array.
- Uniqueness: no two elements in the same array share the key, or the duplicates are handled deliberately.
- Stability: the key does not change when the record’s content is edited, so an edit is reported as a modification of one entity rather than the removal of one and the addition of another.
- Agreement: the team that owns the data confirms the key’s meaning, because a field can look like an identifier without being one.
- Fallback behavior: the diff declares what happens when the key is missing, instead of silently switching to positional matching.
Move detection
jsondiffpatch documents move detection as a refinement applied after its LCS pass. Its stated benefits are a potentially smaller delta, a single move in place of a deletion and a reinsertion, and continued comparison of nested content inside a moved object or array. These are behaviors of that library. They are not guarantees that every diff implementation provides.
The trade-off is in what the consumer must understand. RFC 6902 has a standard move operation, but a delta format from a specific library may encode moves differently. A move is only useful if the system applying the delta reads that format with the same semantics the producer used.
Recommended Free Tools
Comparing the approaches
The following table summarizes how the four common approaches behave on the questions that matter in practice. The entries describe behavior and assumptions, not measured performance.
| Approach | What counts as the same element | Typical result after a reorder | Main risk | Best fit |
|---|---|---|---|---|
| Index-only comparison | Same position | Many spurious replacements | Reports noise for insertions or removals near the start | Arrays where order is the data, such as ranked or sequenced steps |
| LCS with strict equality | Equal primitive values or the same object instance | Good for primitives; separately parsed objects fall back to position | Silent degradation to positional matching for records | Arrays of scalar values, or objects shared by reference in memory |
| LCS with a stable key | Equal value of a chosen identity field | Reordered records match correctly | A weak or non-unique key produces wrong matches | Collections of records with a verified, stable identifier |
| Stable key plus move detection | Equal identity, with moves recorded separately | A reorder becomes one move per relocated record | Producer and consumer must agree on the move format | Systems that store or display minimal change sets for records |
Six questions help decide between these rows. Is array order part of the meaning? Does the schema provide a stable unique key, or can only value and position matching be defended? What happens with duplicate values or missing identifiers? Is the goal a minimal structural patch, a human-readable explanation, or a reliable changed or unchanged result? Are the generated operations applied against the evolving array state? And does the added complexity of identity inference and move detection pay off for this particular dataset?
Troubleshooting common symptoms
- Many replace operations after an insertion near the start. The diff probably fell back to positional matching. Check whether the key is present on every element and whether the matching function is being applied to the objects that were actually parsed.
- A patch fails with an out-of-range array index. Earlier operations changed the array length, and later paths were computed against the original array. Regenerate the operations in an order that accounts for shifts, or track the array state as each operation is produced.
- A relocated record appears as a removal plus an addition. Either the diff has no move detection enabled, or the records lack a stable key, so the diff cannot tell that the two entries are the same entity.
- Two different records are reported as one. The chosen key is not unique in this data set. Add a more specific identity, or handle collisions explicitly rather than letting the first match win.
- A consumer rejects a move or delta. The producer and consumer are using different delta formats or semantics. Standard RFC 6902 operations are the safer interchange choice when several systems must apply the same patch.
Designing a diff you can defend
Decide what an element is before writing the comparison. If order carries meaning, index-based comparison is correct and the noise it produces is the accurate report of a changed sequence. If elements are records, define their identity from the schema, verify it against real data for uniqueness and stability, and state what the diff does when that identity is absent or ambiguous. Record the decision next to the code, because a future reader cannot recover the matching rule from the JSON itself.
When the output will be applied to another copy of the document, generate RFC 6902 operations against the evolving array, and test the patch by applying it to the old document and comparing the result with the new one. When the output is meant for people to read, the patch can be shaped around the records rather than the positions.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Array diffing is hard because the question is not really about arrays. It is about what the application considers the same thing across two versions of its data. The algorithm can only answer that question once someone has specified it.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

