0

Based on what I read from Does JavaScript guarantee object property order?

Object.entries() should guarantee order.

But my project has an Angular website. On the website, I found Object.entries() change orders every time I refresh the page.

At first the keys were:

X-MBX-ORDER-COUNT-1D
X-MBX-USED-WEIGHT-1M
X-SAPI-USED-UID-WEIGHT-1M
X-SAPI-USED-IP-WEIGHT-1M
X-MBX-ORDER-COUNT-10S

After refresh the keys became:

X-MBX-USED-WEIGHT-1M
X-MBX-ORDER-COUNT-10S
X-SAPI-USED-IP-WEIGHT-1M
X-SAPI-USED-UID-WEIGHT-1M
X-MBX-ORDER-COUNT-1D

The object were exactly the same both time.

I can handle website has this problem, but my backend which is Firebase cloud function were written under the assumption that Object.entries() and Object.keys() always have the same order based on the keys.

My questions are:

  1. Does Firebase cloud function guarantee when the keys are same, Object.entries() always has the same order, and is same as Object.keys()?
  2. Why is it different in my Angular website?

tsconfig.json for Angular

/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2017",
    "module": "es2020",
    "lib": [
      "es2020",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "enableI18nLegacyMessageIdFormat": false,
    "strictInjectionParameters": true,
    "strictInputAccessModifiers": true,
    "strictTemplates": true
  }
}

tsconfig.json for Firebase cloud functions

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}

Eidt:

OK I found the problem, but still doesn't understand.

Consider following code:

const x = JSON.parse('{"a": 1, "b": 2, "c": 3}')
const y = JSON.parse('{"b": 2, "a": 1, "c": 3}')
for (const k of Object.keys(x)) {
  console.log(k)
}
console.log("==========")
for (const k of Object.keys(y)) {
  console.log(k)
}

Result:

a
b
c
==========
b
a
c

Is this how it supposed to be?

I directly return the object from Firestore cloud function (onCall), and I noticed the order of the key in the JSON string is different every time.

Jeffrey Chen
  • 1,777
  • 1
  • 18
  • 29
  • You can add a sort for the resulting array. – prasad_ Aug 29 '22 at 13:18
  • 1
    It'll be more likely that someone can help if you edit your question to show the minimal, complete code with which any of us can reproduce the problem. --- "Questions seeking debugging help ('**why isn't this code working?**') must include the desired behavior, a *specific problem or error* and *the shortest code necessary* to reproduce it **in the question itself**. Questions without **a clear problem statement** are not useful to other readers. See: [How to create a Minimal, Complete, and Verifiable example.](http://stackoverflow.com/help/mcve)" – Frank van Puffelen Aug 29 '22 at 13:22
  • @FrankvanPuffelen I currently cannot find a way to regenerate this problem without giving access to my API. If I do JSON.stringfy(obj), copy the result and hardcoded const x = JSON.parse(copied_text) Object.entries(x) doesn't have problem. However const x = JSON.parse(JSON.stringfy(obj_from_my_api)) will have this problem. The keys were the same every time, the value only changes once a minute, but the order changes every few second whenever I refresh. – Jeffrey Chen Aug 29 '22 at 13:47
  • @FrankvanPuffelen I added the code that can produce this problem at the bottom of my question. – Jeffrey Chen Aug 29 '22 at 14:50

1 Answers1

3

Does [it] guarantee when the keys are same, Object.entries() always has the same order, and is same as Object.keys()?

No. "Guaranteed order" means that when you call these functions on the same object multiple times, you always get the same consistent order, and that if you call these functions on multiple objects that were all constructed in the same way, you always get the same reproducible order.

It does not mean that you'll get the same result if you're calling the function on different objects, even if they may have the same set of properties. As the answers to the question you found explain, it depends on the insertion order, and you are supposed to get different arrays for {"a": 1, "b": 2, "c": 3} versus {"b": 2, "a": 1, "c": 3}. Since your cloud function may be called with arbitrary objects, different for every call, you cannot assume anything about the order of their keys.

That said, it would be a bad practice to depend on the order anyway. Key-value collections are fundamentally unordered, if you want to impose an order, sort the array of keys/entries.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375