Skip to content
Astera Technology

Compare JSON objects without accepting different array order

Raw JSON string equality fails harmless key order; broad sorting changes meaningful arrays.

Download complete case notes and answer keys ↓

Define the local evaluation task

A synthetic grader compares finite parsed JSON values. Object key order is ignored; array order, key presence, value types and null remain significant. No numeric-string coercion or removal of extra keys is permitted. Compare parsed structures recursively, rather than raw output text.

  1. Parse candidate before semantic comparison
  2. Ignore only object key presentation order
  3. Retain arrays, types and key presence

Unparsed, cyclic or non-JSON inputs need a separate parser/contract.

Use the filled cases

Original synthetic input and literal answer key
CaseSupplied inputExpected structured outputDecision and reason
Object keys reordered{"expected":{"a":1,"b":["X","Y"]},"answer":{"b":["X","Y"],"a":1}}{"equal":true}Only object presentation order changed.
Array reversed{"expected":{"a":1,"b":["X","Y"]},"answer":{"a":1,"b":["Y","X"]}}{"equal":false}The local array sequence is meaningful and must not be sorted away.
Both comparison operands absent{}{"error":"comparison-operands"}Two absent channels are not two equal finite JSON values.
Extra null-valued key{"expected":{"a":1},"answer":{"a":1,"b":null}}{"equal":false}An extra key remains a structural difference even with null value.
Download editable CSV ↓

Work the extra-null-key case. Which output is required, and why?

Show the worked answer

{"equal":false} — An extra key remains a structural difference even with null value.

Run the self-contained evaluator

Save as .mjs and run with Node.js; supplied fixtures only
import assert from 'node:assert/strict';

function equal(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 Array.isArray(a)&&Array.isArray(b)&&a.length===b.length&&a.every((v,i)=>equal(v,b[i]));
 const ak=Object.keys(a).sort(),bk=Object.keys(b).sort();return equal(ak,bk)&&ak.every(k=>equal(a[k],b[k]));
}
function evaluate(x){if(!Object.hasOwn(x,'expected')||!Object.hasOwn(x,'answer')||x.expected===undefined||x.answer===undefined)return {error:'comparison-operands'};return {equal:equal(x.expected,x.answer)};}

const cases = [
  {
    "id": "keys-reordered",
    "input": {
      "expected": {
        "a": 1,
        "b": [
          "X",
          "Y"
        ]
      },
      "answer": {
        "b": [
          "X",
          "Y"
        ],
        "a": 1
      }
    },
    "expected": {
      "equal": true
    }
  },
  {
    "id": "array-reversed",
    "input": {
      "expected": {
        "a": 1,
        "b": [
          "X",
          "Y"
        ]
      },
      "answer": {
        "a": 1,
        "b": [
          "Y",
          "X"
        ]
      }
    },
    "expected": {
      "equal": false
    }
  },
  {
    "id": "both-operands-absent",
    "input": {},
    "expected": {
      "error": "comparison-operands"
    }
  },
  {
    "id": "extra-null-key",
    "input": {
      "expected": {
        "a": 1
      },
      "answer": {
        "a": 1,
        "b": null
      }
    },
    "expected": {
      "equal": false
    }
  }
];
for (const c of cases) assert.deepEqual(evaluate(c.input), c.expected, c.id);
console.log(JSON.stringify({checked:cases.length,caseIds:cases.map(c=>c.id)}));
Download runnable example ↓

Keep syntax validation separate from this equality criterion. Equal parsed structures can still contain a factually wrong value relative to the user’s source.

Related resources