2

How can I access the counter of a for..in loop?

I have an array and an object. I want to iterate over the object properties while doing the same with the array, without explicitly declaring a counter.

var colors = ['red','yellow','purple','blue'];

var flowers = {'rose':'','sunflower':'','violet':'','hydrangea':''};

for (prop in flowers) {
    flowers[prop] = colors[i];
}

Follow up question. If not possible, how would I create my own for loop with the functionality I require. Here's how it's working currently but I find I'm doing this often and want to create something reusable.

var colors = ['red','yellow','purple','blue'];

var flowers = {'rose':'','sunflower':'','violet':'','hydrangea':''};

var i = 0;
for (prop in flowers) {
    flowers[prop] = colors[i];
    i++;
}
toddsby
  • 454
  • 1
  • 5
  • 17

1 Answers1

4

(ECMAScript 2015 changes things, see update at end of answer.)

I have an array and an object. I want to iterate over the object properties while doing the same with the array, without explicitly declaring a counter.

I don't believe you can. Moreover, it's important to understand the that properties in the object have no order. You seem to be assuming you'll get "rose", then "sunflower", etc. That is simply not guaranteed. Many engines visit object property names in the order in which the properties were added to the object, and the order in which literal properties in an object initializer are added to the object is now (as of ES5 a couple of years back) specified, but visiting them in any particular order in for-in is not specified behavior (similarly, Object.keys is not sorted in any particular way), and a perfectly correct engine can visit them in any order it wants.

As such, with just the array and object you've shown, you have no reliable way to map those properties to the array entries.


As of ECMAScript 2015 (ES6), object properties have order now:

  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
    • 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 property creation order
    • Add P as the last element of keys.
  4. For each own property key P of O that is a Symbol, in property creation order
    • Add P as the last element of keys.
  5. Return keys.

Okay, so we know that they'll be visited in "creation" order, but in what order are they created by an object initializer? Good news: Object initializers are processed in source code order, so that's deterministic. Your rose comes before your sunflower, etc.

This means that while you still can't do what you want without explicitly declaring and maintaining an index variable, you can relate that array and that object reliably:

// Works as of ES6
var colors = ['red','yellow','purple','blue'];

var flowers = {'rose':'','sunflower':'','violet':'','hydrangea':''};

let i = 0;
for (prop in flowers) {
    flowers[prop] = colors[i++];
}

I'm not suggesting doing it, but it's possible now, on compliant engines.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • internally doesn't a for in loop use an iterator, or is that also a poor assumption? My thoughts were if the loop was using an iterator I could access it from inside the for loop. – toddsby Jun 01 '14 at 16:04
  • @toddsby: [The **current** spec just says](http://www.ecma-international.org/ecma-262/5.1/#sec-12.6.4) the engine should make a list and the loop doing the "next" property until done, without specifying the mechanics of that. The ES6 draft defines the algorithm in terms of iterators, but again the properties still have no order (er, unless they've added that somewhere), so even if ES6 defines it in terms of iterators, and even if it gave you access to the iterator, you still can't assume the first property you visit will be "rose". – T.J. Crowder Jun 01 '14 at 16:07
  • How would I create a for loop with the functionality I require? (see updated question) – toddsby Jun 01 '14 at 16:15
  • @toddsby: As I said above: You can't. The `flowers` object has no order, and so it's impossible to relate the properties of the `flowers` object by position. (Side note: I checked, and the April '14 ES6 draft says clearly in §9.1.11 that there is still no order to the properties, so they haven't slipped that into ES6 when I wasn't looking. :-) There's a May draft I don't have time to download and check right now, but I doubt they've changed that.) – T.J. Crowder Jun 01 '14 at 16:17
  • (Got around to the May '14 draft; that language is still there and I very much doubt it'll change.) – T.J. Crowder Jun 01 '14 at 16:46
  • 1
    Thank you for pointing me in the right direction. I wasn't grasping the difference between [enumeration and iteration](http://stackoverflow.com/a/4261096/3150543) in how javascript handles a for in loop. I also found a possible solution here: [How to keep Javascript object/array ordered while also maintaining key lookups](http://stackoverflow.com/a/5773972/3150543) – toddsby Jun 01 '14 at 16:49