Javascript: Iterating over array with non-consecutive keys - javascript

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.

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.

for(var i in messages)
{
console.log(messages[i]);
}

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

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
}

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.

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

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

Related

JavaScript returning numeric value instead of string(s) after sorting array

I am working on an exercise where I prompt the user for a list of names, store the list of names in an array, sort the array in ascending order, and print the list of names (one per line). When I do so, I see a numeric value displayed instead of one name per line. Why is this happening?
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var name in namesArray) {
document.write(name);
}
When you use this construct:
for (var name in namesArray) {
the value of name will be the index in the array (the property name). If you want the actual value in the array, you have to use that property name/index to get the value:
document.write(namesArray[name]);
Of course, you really should not iterate arrays that way in the first place because that iterates all the enumerable properties of the array object (potentially including non array elements) as you can see in this example. Instead, you should use a traditional for loop as in this code example that follows:
var namesArray = [];
do {
var names = prompt("Enter a name: ");
namesArray.push(names);
} while (names != "")
namesArray.sort();
for (var i = 0; i < namesArray.length; i++) {
document.write(namesArray[i]);
}
Other options for iterating the array:
namesArray.forEach(function(value) {
document.write(value)
});
Or, in ES6, you can use the for/of syntax which does actually work how you were trying to use for/in:
for (let value of namesArray) {
document.write(value);
}
You also may want to understand that using document.write() after the document has already been parsed and loaded will cause the browser to clear the current document and start a new one. I don't know the larger context this code fits in, but that could cause you problems.
First, in a for..in loop, here name represents the keys and not the values in your array (you should use namesArray[name])
Also there is another important thing to note. An array is not recommended to be looped through using for..in and if so, you should do it like this:
for (var key in array) {
if (array.hasOwnProperty(key)) {
// then do stuff with array[key]
}
}
The usual preferred ways to loop through an array are the following:
A plain for loop
for (var i = 0, l = array.length; i < l; i++) {
// array[i]
}
Or a higher order function with Array.prototype.forEach (IE9+ if you need compat with IE)
array.forEach(function (item) {
// do something with the item
});

JavaScript for loop Index becomes arrayIndex

I have the following JavaScript code fragment
var ip = new Array();
// This array filled with values and passed to function
function calculateTime(ip) {
for (i in ip) {
window.alert(i);
if (!i in myArray) {
myArray[i] = 0;
} else {
myArray[i] += 1;
}
}
}
I expect i to be an index (0, 1, 2 ...) but sometimes window.alert prints "arrayIndex" and because of that my code doesn't work correctly. Can someone explain me the reason? I am new in JavaScript.
for in will loop over all the enumerable properties of an object.
None of the properties that come with an array are enumerable in modern browsers, but any that are added (such as the normal array indexes or any custom named properties) will be.
Somewhere you have some code that is adding an arrayIndex property to your array and it is coming up when you loop over it.
var myArray = [];
myArray[0] = 1;
myArray[1] = 1;
myArray[2] = 1;
myArray.arrayIndex = 1;
for (prop in myArray) {
console.log(prop);
}
If you only want to get numerical indexes, then use a standard for loop.
for (var i = 0 ; i < myArray.length ; i++) {
console.log(i);
}
For arrays, you should use a numeric variable rather than in:
for(var i = 0; i < ip.length; i++)
in is to iterate over the keys of an Object, but even there you have to take much care to filter out inherited properties.
Now, since arrays are objects too in JavaScript, you can assign them object properties:
ip["arrayIndex"] = 'some value';
Then "arrayIndex" will show up in a for...in iteration, whereas in a "normal" for loop, it won't.
Use either
for(var i=0; i<ip.length; i++){
//your code
}
or
ip.forEach(function(val,i){
// your code
});
The for(var x in y) loop works best for Object rather than Array. When you use it on arrays it will loop through all properties including named ones like length not just numerical indices.

Iterating JavaScript object with strings as keys

I am building a JavaScript array, which has strings as keys.
The array should have an object at every entry. My object looks like this (a console.log of my variable rIds):
Now, the length of this object is 0, which makes it impossible to iterate it.
I want to iterate every key, so I can retrieve and access my list of ids (rIds[key].productIds).
Code:
var rIds = [];
var response = RR.data.JSON.placements;
console.log(response);
$(response).each(function (placementId) {
var placement_name = this.placement_name;
rIds[this.placement_name] = {
productIds: []
};
$(this.recs).each(function (recIndex) {
var pid = this.pid;
pid = getRandomId(19341610, 19341746);
rIds[placement_name].productIds[recIndex] = pid;
});
});
var count = '<%=ViewBag.Get("RecommendationCount") %>';
console.log(rIds);
console.log(rIds.length);
$.each(rIds, function (index, val) {
console.log(val); // This should work as expected.
});
What did I try?
I found the following snippet, which does not work in IE8. However, this does not really help be, even though Object.keys(rIds).length results in the correct length.
for (var index = 0; index < Object.keys(rIds).length; index++) {
console.log(rIds[index]);
}
However, this code does not make my access the object.
Tl;dr & question
How do I iterate my JavaScript object which has strings as keys, so I can access the individual entries?
The Object.keys() function returns an array containing the property names of the object.
var keys = Object.keys(rIds);
for (var i = 0; i < keys.length; ++i) {
console.log(rids[keys[i]]); // object value
}
Alternatively, you can use the .forEach() method:
Object.keys(rIds).forEach(function(key) {
console.log(this[key]);
}, rIds);
Finally there's the venerable for ... in loop:
for (var key in rIds) {
if (rIds.hasOwnProperty(key))
console.log(rIds[key]);
To support versions of IE before IE9 you're kind-of stuck with for ... in, though you can find mostly-correct "polyfills" for Object.keys() and Array.prototype.forEach() at MDN.
To supplement Pointy's answer, you can also use jQuery's $.each (since you're already using jQuery elsewhere) to iterate over each key/value pair in your rIds object:
$.each(rIds, function(key, value) {
console.log(value);
});

Why on earth is jack showing up in the sequence of alert boxes?

Object.prototype.jack = {};
var a = [1,2,3];
for(var number in a){
alert(number);
}
Could anyone tell me why the word "jack" jumped out of the alert box?
Thank you very much!
Simple - arrays are objects in javascript so by adding:
Object.prototype.jack={};
You've added an enumerable property 'jack' to all objects (and by that to all arrays).
once creating your array "a" and looping through all of its properties
for(var number in a)
You are sure to get a 'jack' alert. To avoid it showing up you can use .hasOwnProperty() to make sure all alerted properties are not inherited. or use a regular
for(var i=0; i < a.length; i++)
loop.
The for..in loop iterates over properties. It's only recommended (by Mozilla and other authorities) to be used on Objects, but not Arrays. If you insist, this is the correct way to iterate over an object and it will work for an array (most of the time).
for (var number in a) {
if (Object.prototype.hasOwnProperty.call(a, number)) {
alert(a[number]); // shows 1 then 2 then 3
}
}
To do it the generally accepted way,
for (var i=0; i<a.length; i++) {
alert(a[i]); // same as above
}
My personal preference is:
for (var val, i=0; val = a[i]; i++) {
alert(val); // same as above
}
Because you're not checking if jack is a property inherited from prototype or not. Try this instead:
Object.prototype.jack={};
var a = [1,2,3];
for (var number in a) {
if (a.hasOwnProperty(number)) {
alert(number);
}
}

Javascript for loop var "i" is treated as a string?

I am using Titanium to build some mobile apps and I noticed that this will give a result that I wasn't expecting.
data = ['a','b', 'c','d'];
for (var i in data){
Ti.API.debug(i+1);
};
This will print: 01,11,12,13
Is this something particular to Titanium or is it generally in Javascript?
Why isn't 'i' being treated as an integer? I am very confused.
Thanks for your help.
This doesn't directly answer your question, but if you are looping through an array you should not use for (var i in data). This loops through all members of an object, including methods, properties, etc.
What you want to do is this:
for (var i=0, item; i<data.length; i++) {
item = data[i];
}
data is an array, so you use a for loop, not a for-in loop:
var data = [ ... ];
var i;
for ( i = 0; i < data.length; i += 1 ) {
Ti.API.debug( i + 1 );
}
Alternatively, you can use the forEach array method:
data.forEach( function ( val, i ) {
Ti.API.debug( i + 1 );
});
The reason why you see this behavior is that the type of i when using a for-in over an array is string not int. Hence the + is doing string concatenation and not addition. If you want it to be the numerical value then use a for loop
for (var i = 0; i < data.length; i++) {
Ti.API.debug(i + 1);
}
Try this:
data = ['a','b', 'c','d'];
for (var i in data){
Ti.API.debug(i*1+1);
};
Multiplying i x 1 will force it to recognize it as numeric.
Try this:
Ti.API.debug(parseInt(i)+1);
You can do
data = ['a','b', 'c','d'];
for (var i in data){
console.log(parseInt(i)+1);
};
But it is not recommended. Because in Javascript for..in loop is for key:value pairs (Objects). So if use it with an array each index is converted to string as key.
so always use for(i = 0; i < length; i++) with arrays.
This is because javascript handles for-each loop this way.
In other languages for(i in datas) will loop through each data.
But in javascript the i will have index value instead of data. so you have to get data by datas[i].

Categories

Resources