9

I have a sparse array whose contents aren't guaranteed to be inserted in index order but need to be iterated through in index order. To iterate through a sparse array I understand that you need to use a for..in statement.

However, according to this article:

There is no guarantee that for...in will return the indexes in any particular order

But stackoverflow questions like this suggest that whilst object property orders are not guaranteed, array orders are:

properties order in objects are not guaranted in JavaScript, you need to use an Array.

I tested this in the latest versions of Chrome, Firefox and IE.

<ol id="items"></ol>
var list = [];

function addItem(index) {
    list[index] = { idx : index };
}

var insertOrder = [ 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 ];

for ( var i = 0; i < 15; i++ ) {
    addItem(insertOrder[i]);
}

for(var item in list) {
    $("#items").append("<li>" + list[item].idx + "</li>");
}

All appear to honor the index order so can I trust this always to be the case? Otherwise, how do I best get them in index order?

Community
  • 1
  • 1
Chris Simpson
  • 7,411
  • 8
  • 42
  • 66
  • I wouldn't take that line you quoted as an indication that `for..in` order is guaranteed with arrays. (I'm not saying that it isn't, but that line isn't really saying that it is). Plus, there are [very good reasons](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such-a-bad-idea) to avoid using `for..in` with arrays, even if the ordering works out ok. – JLRishe Dec 22 '14 at 07:46
  • You can see the differences here: http://jsfiddle.net/abhitalks/rfe33uxf/ – Abhitalks Dec 22 '14 at 07:52
  • c.f. [this answer](http://stackoverflow.com/a/4261096/1945651): _"Also, the order of iteration is not guaranteed by the spec., meaning that if you want to "iterate" an array object, with this statement you cannot be sure that the properties (array indexes) will be visited in the numeric order."_ – JLRishe Dec 22 '14 at 07:57
  • Where is `idx` defined in your function `addItem`? – Ash Sep 19 '16 at 04:42

2 Answers2

12

MDN has the answer to your original question:

Note: for..in should not be used to iterate over an Array where index order is important.

Array indexes are just enumerable properties with integer names and are otherwise identical to general Object properties. There is no guarantee that for...in will return the indexes in any particular order and it will return all enumerable properties, including those with non–integer names and those that are inherited.

You don't have to use for..in to iterate through a sparse array, and you should definitely avoid doing so if you can.

You can just use .forEach:

list.forEach(function (el) {  // add a second parameter if you need the indices
    $("#items").append($("<li>").text(el.idx));
});

forEach is defined to iterate in index order and only include elements that are present in the array:

forEach executes the provided callback once for each element present in the array in ascending order. It is not invoked for indexes that have been deleted or elided. However, it is executed for elements that are present and have the value undefined.


If you're targeting environments that don't support forEach, you can use the following, or the shim provided on that MDN page:

for (var i = 0; i < list.length; i += 1) {
    if (i in list) {
        $("#items").append($("<li>").text(list[i].idx));
    }
}
Community
  • 1
  • 1
JLRishe
  • 90,548
  • 14
  • 117
  • 150
  • Thanks. And [this post](http://stackoverflow.com/questions/13600922/does-javascript-array-foreach-traverse-elements-in-ascending-order) tells me that ForEach is guaranteed. – Chris Simpson Dec 22 '14 at 08:29
1

The for...of construct in ECMAScript 2015 iterates over array data only (not prototype methods) and guarantees order. See MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/fBlockquoteor...of and this answer: Does "for...of" loop iteration follow the array order in JavaScript?

See Access to ES6 array element index inside for-of loop for retrieving indexes. Note that for..of iterates over non-defined holes / empty values in arrays, so you need to check if key in array if you don't want these.

Further discussion here: https://groups.google.com/forum/#!topic/strengthen-js/jj7UX-fU-_A - suggests javascript is missing an ordered map data structure.

Simon D
  • 3,761
  • 5
  • 33
  • 46