Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iteration order of for.in – not by insertion (any more?)

Tags:

According to my research, the order of keys in a for..in loop should be undefined/unreliable – but, if left undisturbed, should be in insertion order – but it's not:

I fetch this data object from the database, ordered by name:

var travel = {  
    '2': { name: 'bus',  price: 10 },  
    '3': { name: 'foot', price: 0 },  
    '1': { name: 'taxi', price: 100 }  
}  
for (way in travel) console.log( travel[way].name ) // => taxi, bus, foot  

The keys get ordered numerically (in all of Chrome, Firefox, and Edge). Why?

And (since I was wrong) how can I iterate through them ordered by .name?

like image 252
T4NK3R Avatar asked Mar 04 '17 11:03

T4NK3R


People also ask

Which order is correct for a for loop?

So first the condition is checked, then the loop body is executed, then the increment.

What is the difference between for of and for in?

The second and the biggest difference between both of these statements are, by default, the for...in iterates over property names and the for...of iterates over property values. For instance, let's say we have the following array.

Does a for loop start at 0 or 1?

In this case, the input should be a number ( num ). The for loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input num , and an iterator that adds 1 to the counter each time.


1 Answers

According to my research, the order of keys in a for..in loop should be undefined/unreliable

Undefined, yes.

  • but, if left undisturbed, should be in insertion order

No, you were right the first time: It's undefined. Even in ES2015 (aka "ES6") and above, which do provide property order for some other operations, the older operations for-in and Object.keys are not required to follow the order defined for the new ones.

In those other operations (Object.getOwnPropertyNames, JSON.serialize, ...), the order (defined here) isn't purely insertion order: Properties whose names are array indexes according to the spec's definition* come first, in numeric order. Most major JavaScript engines have updated their handling of for-in to match their handling of these new operations (many already did treat array indexes differently, but they varied in terms of whether they put those before the non-array-indexes or after), but again, it's undefined, and you shouldn't rely on it.

If you want pure insertion order, ES2015's Map provides that, regardless of the value of the key. Objects don't.

Here's an example using Map:

const map = new Map([
  ['2', { name: 'bus',  price: 10 }],
  ['3', { name: 'foot', price: 0 }],
  ['1', { name: 'taxi', price: 100 }]
]);
for (const entry of map.values()) { // bus, foot, taxi
  console.log(entry.name);
}

* The spec's definition of an "array index" is:

An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 253-1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 232-1.

like image 81
T.J. Crowder Avatar answered Sep 23 '22 10:09

T.J. Crowder