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?
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));
}
}
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.