cFreed
cFreed

Reputation: 4484

Does JSON.parse() really sort properties when the key names have numeric values?

There's a number of posts here about this issue, and they all contain a lot of assertions that can be summarized like this:

  1. Object properties are never guaranteed to be ordered in any way.
  2. JSON.parse() never sorts properties in any way.

Obviously we tend to have no doubt about #1 above, so we may reasonably expect that, for any operation, properties are processed merely in the order they appear.
[edit, following the @Bergi's comment: or at least they should appear in a random order]

Then from that we might especially infer that #2 should be true.

But look at this snippet:
(BTW note: to show the results, snippets below don't use console.log() which may itself change order of the output. Instead objects are iterated by for (key in obj) and the output displayed in the document)

var inputs = [
  '{"c": "C", "a": "A", "b": "B"}',
  '{"3": "C", "1": "A", "2": "B"}',
  '{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}'
];

for (var i in inputs) {
  var json = inputs[i],
      parsed = JSON.parse(json),
      output = [];
  for (var j in parsed) {
    output.push(j + ': ' + parsed[j]);
  }
  document.write(`JSON: ${json}<br />Parsed: ${output.join(', ')})<hr />`);
}

It shows that, given a JSON string having unordered keys:

From that I was first tempted to conclude that actually there would be a (non-documented?) feature, so JSON.parse() works following the "rules" exposed above.

But I had the idea to look further, so the snippet below now shows how ordered are the properties of a merely coded object:

var objects = [
  [
    '{"c": "C", "a": "A", "b": "B"}',
    {"c": "C", "a": "A", "b": "B"}
  ],
  [
    '{"3": "C", "1": "A", "2": "B"}',
    {"3": "C", "1": "A", "2": "B"}
  ],
  [
    '{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}',
    {"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}
  ]
];

for (var i in objects) {
  var object = objects[i],
      output = [];
  for (var j in object[1]) {
    output.push(j + ': ' + object[1][j]);
  }
  document.write(`Code: ${object[0]}<br />Object: ${output.join(', ')}<hr />`);
}

It results in analogue observations, i.e. whichever order they're coded, properties are stored following the 3rd rule above:

So it means that JSON.parse() is not involved: in fact it seems to be a fundamental process of object building.
Again this appears not documented, at least as far I could find.

Any clue for a real, authoritative, rule?


[Edit, thanks to @Oriol's answer] It actually appears that, synthetically:

Upvotes: 4

Views: 3467

Answers (2)

Oriol
Oriol

Reputation: 288520

The properties of an object have no order, so JSON.parse can't sort them. However, when you list or enumerate the properties of an object, the order may be well-defined or not.

Not necessarily for for...in loops nor Object.keys

As fully explained in Does ES6 introduce a well-defined order of enumeration for object properties?, the spec says

The mechanics and order of enumerating the properties is not specified

But yes for OrdinaryOwnPropertyKeys

Objects have an internal [[OwnPropertyKeys]] method, which is used for example by Object.getOwnPropertyNames and Object.getOwnPropertySymbols.

In the case of ordinary objects, that method uses the OrdinaryGetOwnProperty abstract operation, which returns properties in a well-defined order:

When the abstract operation OrdinaryOwnPropertyKeys is called with Object O, the following steps are taken:

  1. Let keys be a new empty List.
  2. For each own property key P of O that is an integer index, in ascending numeric index order
    1. Add P as the last element of keys.
  3. For each own property key P of O that is a String but is not an integer index, in ascending chronological order of property creation
    1. Add P as the last element of keys.
  4. For each own property key P of O that is a Symbol, in ascending chronological order of property creation
    1. Add P as the last element of keys.
  5. Return keys.

Therefore, since an order is required by OrdinaryOwnPropertyKeys, implementations may decide to internally store the properties in that order, and use it too when enumerating. That's what you observed, but you can't rely on it.

Also be aware non-ordinary objects (e.g. proxy objects) may have another [[OwnPropertyKeys]] internal method, so even when using Object.getOwnPropertyNames the order could still be different.

Upvotes: 3

Bergi
Bergi

Reputation: 665030

so we may reasonably expect that, for any operation, properties are processed merely in the order they appear

That's where the flaw in the reasoning lies. Given that object properties aren't guaranteed to be ordered, we have to assume that any operation processes properties in any order that it sees fit.

And in fact engines evolved in a way that treat integer properties specially - they're like array indices, and are stored in a more efficient format than a lookup table.

Upvotes: 0

Related Questions