0

I need to create one Object where the keys are strings composed of numbers but I couldn't sort them in the correct way:

let simple_arr = ['00', '11', '22', '33'];
let simple_obj = {};

for (let it of simple_arr) {
  simple_obj[it] = "anything";
}

for (let it2 in simple_obj) { 
  console.log(it2)
}

The output that I would like is: 00, 11, 22, 33.

But it reproduces: 11, 22, 33, 00.

I already tried to sort this object with Object.keys, but always when I set an iterator on it the value "00" goes to the end.

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
Guilherme B
  • 75
  • 1
  • 6
  • The *keys* of a standard array are *always* strings or Symbols. The *indexes* of a standard array are always numeric strings. What you mean is that the *values* of the array are numeric strings. – T.J. Crowder Nov 07 '19 at 17:42
  • Use a [**`Map`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) instead of an object. – ibrahim mahrir Nov 07 '19 at 17:43
  • Please read [Does JavaScript Guarantee Object Property Order?](https://stackoverflow.com/a/38218582/3082296) and https://2ality.com/2015/10/property-traversal-order-es6.html – adiga Nov 07 '19 at 17:54

3 Answers3

4

You cannot rely on the output you've said you want using a for-in loop. Although object properties have order as of ES2015, for-in loops don't. (Neither does the array from Object.keys.)

If you want order, you want an array. (But you can use a Map, too, in some situations; see below.)

Moreover, even with ES2015's property order, the keys you've shown will never be in the order you've shown, because '00' is different from '11', '22', and '33': It's not a canonical number String. The ES2015 order (for the operations that support it) is properties whose names are strings in canonical number String format, in order numerically, followed by other string-named properties in the order they were added, followed by Symbol-named properties.

So your object will never behave as you've said you want. Use an array.

(Of course, if you introduce a prefix so none of them is in canonical number String format and add the properties in the order you want them, that would work with Object.getOwnPropertyNames and other operations that support the order. But I really wouldn't do that.)

Another option is to use a Map, which unlike objects always follows the order that the keys were first used.¹ If you create a Map like this:

const map = new Map(simple_arr.map(key => [key, "anything"]));

...then looping through that map will reliably visit the '00' entry, then the '11' entry, then the '22' entry, then the '33' entry:

const simple_arr = ['00', '11', '22', '33'];
const map = new Map(simple_arr.map(key => [key, "anything"]));
for (const [key, value] of map.entries()) {
    console.log(key, value);
}

¹ That's the simple version. The longer version: If you add a key/value to the map and that key is not already in the map, it goes at the end of the list of the map's keys. If you set a new value for that key, it doesn't move in that list. If you remove the key and then add it again later, it goes at the end again.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
3

Objects have no concept of ordering when it comes to their keys. Even if you could achieve this with this particular data set in your particular browser, there's no guarantee the keys will come out in the order you want.

If you really want to achieve this, you should sort the keys returned from Object.keys() and iterate over that.


My original answer above is incorrect. As of ES2015, object keys do have a specific order. I still maintain that trying to rely on the order of keys of an object is probably a bad idea - Use a Map to retain the original values of your keys (without them being coerced to a string) and order the keys when iterating, instead.

Dan
  • 10,282
  • 2
  • 37
  • 64
  • Ah, this is new - I just found [this](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order/38218582#38218582) answer, which refutes mine. – Dan Nov 07 '19 at 17:43
  • ES6 isn't exactly new, but i know that 90% of SO still believes in the ES5 spec. Make a question about object property order, you'll get an upvoted "there is none" answer within the first minute. Anyways, it's probably better like that - then at least there won't be 392454 people using property order in silly situations :) – ASDFGerte Nov 07 '19 at 17:46
  • It's new enough that I didn't know this very specific detail of the spec. It's not like there was a lot of fanfare about it. I'm happy to revise my answer (and I have) to be accurate. – Dan Nov 07 '19 at 17:47
  • @DanPantry - Depends on where you hang out. :-) There was a fair bit [here](https://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties) and [here](https://stackoverflow.com/questions/5525795/does-javascript-guarantee-object-property-order). – T.J. Crowder Nov 07 '19 at 17:50
1

Since ES2015 object keys do have a traversal order:

  • Strings that are legitimate numbers (Integer indices) come first, and ordered by their value
  • Strings that aren't are placed after numeric strings, and their order is defined by the order of entry

Convert 00 to 0 so it will be ordered by the numeric value:

const simple_arr = ['0', '11', '22', '33'];
const simple_obj = {};
for(let it of simple_arr){
  simple_obj[it] = "anything";
}

for(let it2 in simple_obj){ 
  console.log(it2)
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209