9

I need to iterate over an array for which the keys are non-consecutive:

var messages = new Array();
messages[0] = "This is the first message";
messages[3] = "This is another message";

Obviously using the index of a for loop will not work as it depends on the keys being sequential:

for (var i=0 ; i<messages.length ; i++) {
    alert(messages[i]); // Will only alert the first message, as i is never equal to 3
}

What is the canonical way of dealing with this, seeing as the for-each syntax is not intended for iterating over values in an array in javascript? Thanks.

5
  • 1
    How did you end up with such an array in the first place? Instead of looking for some hacks to iterate over it why don't you tackle the problem at its root => which is the way you obtain this array. Commented Jan 30, 2012 at 15:44
  • 1
    Have you considered using an Object instead? { "0": "this is the first message", "3": "this is another message"} Commented Jan 30, 2012 at 15:45
  • 5
    Will only alert the first message, as i is never equal to 3 this is not correct. messages.length will be 4. Commented Jan 30, 2012 at 15:48
  • The array represents object, not simple string literals as in the example. The array keys are the database primary keys. Having the primary keys as another property of the object leads to all kinds of complications as I usually know which object to access by ID, so having the ID as the array key is very convenient. That is, other than this one issue. Commented Jan 30, 2012 at 16:36
  • @Yoshi: You are right, the loop must be throwing an exception and quitting when it hits the invalid array key. Either way, the approach won't work! Commented Jan 30, 2012 at 16:39

7 Answers 7

10

The idiomatic way would be to use an object, not an array. Just be sure to check hasOwnProperty to make sure you don't pick up stray things which may have been added to the prototype.

var messages = { };
messages[0] = "This is the first message";
messages[3] = "This is another message";

for (var i in messages) {
    if (messages.hasOwnProperty(i))
        alert(messages[i]); 
}

Or, the more modern way would be to use Object.keys

Object.keys(messages).forEach(prop => {
    alert(messages[prop]);
});

Be sure to transpile that code with Babel if you plan on running it in older browsers like IE.

Sign up to request clarification or add additional context in comments.

5 Comments

... squeezing in a hasOwnProperty() check would increase the awesomeness of this answer.
Why not call hasOwnProperty on the messages directly? if( messages.hasOwnProperty( i ) ) { alert( messages( i ) ); }
@meouw - it's just an extra-safe, though probably unnecessary precaution to protect you from the slim possibility that messages has defined a property or function named hasOwnProperty - {}.hasOwnProperty.call ensures that you'll always get the one on object.prototype
OK - I think it clouds the issue a bit though. You're answer is not as clear as it could be because you are addressing not only the OP's question but another issue as well.
Thank you for answering this question, it still confuses me how some of the stack community decide to start asking irrelevant questions or telling your your wrong but fail to give you good reasoning.
5
for(var i in messages)
{
    console.log(messages[i]);
}

1 Comment

Thank you, I see that console.log() is quite a powerful tool!
3

You could ignore the undefined properties...

for (var i=0 ; i<messages.length ; i++) {
    if(messages[i] !== undefined)
        alert(messages[i]);
}

Or use forEach, which will ignore undefined undeclared properties...

messages.forEach(function(v,i) {
    alert(v);
});

5 Comments

ohhh - +1 for forEach - didn't know that ignored undefined properties.
forEach is not implemented in IE < 9 though.
@Tim - true...I guess I just (usually) have the luxury of ignoring that fact :)
To be clear, forEach does not ignore elements with a value of undefined. If you say messages[n] = undefined and then call messages.forEach(), the element at n will be processed. Instead, forEach skips indexes for which no element exists in the Array. The same logic can be used by testing if (i in messages).
@gilly3: You're right. Undeclared would have been a better word.
2

Simple! if the array has regular gaps between the indices do this:

for (var i = 0 ; i < messages.length; i += gap) {
    alert(messages[i]); // Will only alert the messages at the regular interval/gap 
}

2 Comments

Though this would work for a gap that would be constant, the OP states that the keys are non-consecutive, which is not the same as having a constant gap.
Not the same but includes this case, then it is the code for it, which I clearly stated at the very beginning of my answer.
1

You can use each() jQuery method to do this.

$.each(messages, function(index, val){
    alert(val); 
});

From jQuery docs

each()

A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.

4 Comments

Is "jQuery" really the best solution for simple enumeration?
@ShankarSangoli, and you find that $.each(messages, function(index, val){ is simpler than for (var i in messages) {? I guess we have different definitions of simplicity.
I think using the fundamental language construct "for" is both cleaner and simpler than using jQuery.each.
Because jQuery gives the index and value seperated as callback arguments, you don't have to maintain variables. If performance is the concern I would definitely go with classic JavaScript way using for or forEach.
1

When you create an array and give it values at 0 and 3, undefined values are created at 1 and 2. try this:

$.each(messages, function(i,val) { 
  if (val) {
    alert(val);
  } 
});

5 Comments

Is "jQuery" really the best solution for simple enumeration?
That depends on what you consider "best". For someone that is learning javascript and has access to jquery, using the jquery each can avoid other common issues found when using a for loop without knowing how they work.
If all you look at is "fastest", it would be best to never use a javascript library, right?
@Matt - the real answer here is that when you create an array with values at 0 and 3, the indexes at 1 and 2 are populated and iterated through. His original code will alert all messages. How you iterate over them doesn't matter.
When you create an array with values at 0 and 3, there are no elements at 1 or 2. It's not that the elements at 1 and 2 contain a value of undefined. There simply are no elements at indexes 1 and 2. Try this in a JavaScript console: var a = [1]; a[3] = 1; console.log(1 in a);
0

For a use case such with the assumptions:

array.length >== 1 (i.e: an array with meaningful data in it already)

Where you are interested in the data from array[1], array[15], array[45] etc

You can do something similar to:

var array = ["you","will","become","strong","with","the","codes","padawan"];
var values = [1,5,7];


for (var i = 0; i < values.length; i++){
    var cypher = values[i];
    console.log(array[cypher]);
}
//will, the, padawan

Or perhaps something more meaningful such as:

for (var i = 0; i < values.length; i++){
    var cypher = values[i];
    aService.aFn.(array[cypher],cb);
}
//calls aService.aFn separately for each value array[1] , array[5] , array[7] passed as args

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.