Oct 3, 2026 · 5 min read
The docs explain the three array comparison methods in a paragraph each. That’s enough to pick one — but not enough to predict what you’ll actually see when the diff comes back. This post runs the same data through all three methods and shows the exact output each produces, because the differences are bigger than the names suggest.
Quick recap of the mechanics (details in how the engine works):
a[i] with b[i] and recurses on each pair.Same option, three very different answers.
An event log gains a single entry at position 1:
{ "items": [1, 2, 3, 4] }
{ "items": [1, 5, 2, 3, 4] }
By Index sees positional alignment shift and reports a cascade:
valueChanged items[1]
valueChanged items[2]
valueChanged items[3]
added items[4]
Four differences for one insertion — technically correct, practically useless. Everything after the insertion point “changed”.
LCS finds the common subsequence [1, 2, 3, 4] and reports exactly what happened:
added items[1]
Unordered also nails this one — every base element finds a match, one contrast element is left over:
added items[1]
Winner: LCS or Unordered. By Index is only honest here if position is genuinely meaningful.
Same elements, different order:
{ "items": [1, 2, 3] }
{ "items": [3, 1, 2] }
By Index reports every position as changed:
valueChanged items[0]
valueChanged items[1]
valueChanged items[2]
LCS keeps the longest ordered run it can ([1, 2]) and moves the rest:
deleted items[2]
added items[0]
Unordered says nothing at all:
(no differences)
Which answer is right depends entirely on your data. Reordered steps in a deployment pipeline? By Index’s alarm is correct — order is meaning. Reordered tags on a blog post? Unordered’s silence is correct. This is the scenario where picking the wrong method either floods you with noise or hides a real regression.
Here’s the trade-off the docs don’t spell out. An object’s field changes inside an array:
{ "users": [{ "id": 1, "role": "admin" }, { "id": 2, "role": "user" }] }
{ "users": [{ "id": 1, "role": "admin" }, { "id": 2, "role": "owner" }] }
By Index recurses into the pair and pinpoints the field:
valueChanged users[1].role
LCS matches elements by deep equality — and { "id": 2, "role": "user" } is not deep-equal to { "id": 2, "role": "owner" }. So the element falls out of the subsequence entirely:
deleted users[1]
added users[1]
Unordered behaves the same way:
deleted users[1]
added users[1]
Same index, no hint about what inside changed. If your arrays contain objects and you care about field-level changes, LCS and Unordered lose information — they can’t say “role changed from user to owner”, only “something at [1] is gone, something new is at [1]”. By Index is the only method that preserves field-level precision.
Unordered is often described as “set comparison”, but the implementation is a multiset comparison: each match is consumed one-for-one.
{ "tags": [1, 2, 2, 3] }
{ "tags": [3, 1, 2] }
The two 2s on the left can only consume one 2 on the right:
deleted tags[2]
If duplicates are significant in your data, this is exactly what you want. If you genuinely want set semantics (“same unique values”), you should deduplicate before comparing — or know that Unordered will flag count differences.
The three methods also differ in cost, which matters on large arrays:
| Method | Time complexity | Notes |
|---|---|---|
| By Index | O(n) comparisons | Plus one recursive comparison per pair |
| LCS | O(m × n) deep-equality checks | Each DP cell may recursively compare two entire subtrees |
| Unordered | O(m × n) worst case | Every unmatched base element scans the contrast side |
For a hundred-element array you’ll never feel any of this. For a 10,000-element array of nested objects, LCS’s DP table — with a full recursive comparison in every cell — is genuinely expensive, and Unordered’s quadratic scan isn’t far behind. By Index scales linearly and is the only method that’s essentially free at any size.
| Your array is… | Best method | What you give up |
|---|---|---|
| Positional (tuples, time series, table rows) | By Index | Insertions shift everything — noise on misaligned lists |
| Ordered, occasionally edited (logs, changelogs) | LCS | Field-level precision on modified elements; O(m·n) cost |
| Orderless (tags, permissions, flags) | Unordered | Order changes are invisible; whole-element add/delete reporting |
The one-line version: By Index is precise but brittle, LCS is minimal but shallow, Unordered is calm but blind to order.
Try it on your own arrays — the method switch lives in Settings, and you can flip it and re-run the comparison in seconds: compare two JSON files now.