structura.Runs in your browser

JSON field notes · 03

Compare JSON while ignoring array order

Two API responses can hold the same data in a different order. Object key order never matters in Structura; for arrays you choose between position, an unordered comparison and matching items by an id. The right choice depends on what each array means.

Object key order is always ignored

{"a": 1, "b": 2} and {"b": 2, "a": 1} produce no differences. Properties are matched by name, and indentation, line breaks and spacing between tokens are not compared. You do not need an option for this, and none of the array settings below change it.

One example, four modes

An account export with three arrays: tags (a set of labels), roles (objects with an id) and steps (an ordered checklist). In B, the tags and roles are reordered, a role gains a scope, and two steps swap places.

A · Original

{
  "tags": ["beta", "admin", "beta"],
  "roles": [
    {"id": "r1", "name": "Viewer"},
    {"id": "r2", "name": "Editor"}
  ],
  "steps": ["build", "test", "deploy"]
}

B · Updated

{
  "tags": ["admin", "beta", "beta"],
  "roles": [
    {"id": "r2", "name": "Editor", "scope": "all"},
    {"id": "r1", "name": "Viewer"}
  ],
  "steps": ["build", "deploy", "test"]
}
OptionsChangesWhat the report shows
Default (by position)9tags[0], tags[1]; id and name of both roles plus an added scope at roles[0]; steps[1], steps[2]. Accurate but noisy: the roles look like they were edited.
Ignore array order1$["roles"] changed as a whole. Tags are equal as a collection. The swapped steps are hidden, because this option applies to every array.
Match array items by id5$["roles"][?@["id"]=="r2"]["scope"] added. Tags and steps are still compared by position, so they report 2 changes each.
Both options1Only the added scope. Roles are paired by id; tags and steps are compared without order, so the step swap is again hidden.

The most useful view here is Match array items by id: the role change is named exactly, and the step swap stays visible. Ignore array order is the right choice only when no array in the document has a meaningful order.

Try the four modes on your JSON

Which mode to use

Your arrays holdUseBecause
Ordered data: steps, rankings, time series, coordinatesPosition (default)A move is a real change and should be reported.
Records with an identifier: users, line items, rolesMatch array items by id (or sku, uuid…)Each record is compared with its own counterpart, and a deletion does not shift the rest.
Sets of plain values: tags, permissions, feature flagsIgnore array orderOnly membership matters, and duplicates still count.
A mix, with no ordered arrays that matterBothRecords are paired by id; other arrays are compared as collections.

If timestamps or request ids differ on every response, add them to Ignore keys as well. Ignored keys are skipped inside unordered arrays too, so two items that differ only in updatedAt count as equal.

Exact rules

Alternative: sort before comparing

If you need the comparison in a script, you can normalise both files first and then compare them as text. With jq, -S sorts object keys and walk sorts every array:

jq -S 'walk(if type == "array" then sort else . end)' a.json > a.sorted.json
jq -S 'walk(if type == "array" then sort else . end)' b.json > b.sorted.json
diff a.sorted.json b.sorted.json

This has the same drawback as Ignore array order: every array loses its order, including ones where order matters. And a changed record sorts to a different position, so a text diff shows it as removed and added instead of naming the field that changed.

Questions

Does JSON object key order affect the comparison?

No. Object properties are matched by name in every mode, and whitespace and indentation are ignored. Only arrays have an order that can matter.

Can I ignore order for one array but not another?

Not with Ignore array order, which applies to every array at every depth. Match array items by is more selective: it only pairs arrays in which every item on both sides has a unique value for that property, and other arrays keep the positional or unordered setting.

Are duplicate array items counted when order is ignored?

Yes. Arrays are compared as collections that keep duplicates, so [1, 1, 2] and [1, 2, 2] are different, while [1, 2, 2] and [2, 1, 2] are the same.

Why does an unordered array show as one change?

Without positions or ids there is no reliable way to say which item became which, so the whole array is reported once with its before and after values. Use Match array items by when the items have an identifier.

Are 1 and "1" treated as the same id?

No. Ids keep their JSON type, so the number 1 and the string "1" are different ids, just as they are different values elsewhere in the report.

Related