This would be a very easy question for all you javascript/jquery guys.
I've been programming javascript from past 2 years and today I faced a strange problem.
I am fetching a JSONarray from my C# Webmethod
and calling it by jQuery.ajax()
When I do
for (i in JSONRaw) {
Index = Index + 1;
$('#bottomGridDashboard').append('<tr> <td>' + Index + '</td> <td>' + JSONRaw[i].DisplayName + '</td> <td>' + JSONRaw[i].SpeedInKPH + '</td> <td><img src="' + JSONRaw[i].ImageURL + '" height="20px" alt="" /></td> <td>' + JSONRaw[i].DepotID + '</td> <td>' + JSONRaw[i].RouteID + '/' + JSONRaw[i].ScheduleID + '/' + JSONRaw[i].TripID + '</td> <td>' + JSONRaw[i].Direction + '</td> <td>' + JSONRaw[i].LastStop + '</td> <td> ' + JSONRaw[i].ETAMinutes + ' </td> </tr>');
}
the appended table adds two extra rows putting every field as 'undefined'. See this Image
However if i replace the loop with
for (var i = 0; i < JSONRaw.length;i++ ) {
Index = Index + 1;
$('#bottomGridDashboard').append('<tr> <td>' + Index + '</td> <td>' + JSONRaw[i].DisplayName + '</td> <td>' + JSONRaw[i].SpeedInKPH + '</td> <td><img src="' + JSONRaw[i].ImageURL + '" height="20px" alt="" /></td> <td>' + JSONRaw[i].DepotID + '</td> <td>' + JSONRaw[i].RouteID + '/' + JSONRaw[i].ScheduleID + '/' + JSONRaw[i].TripID + '</td> <td>' + JSONRaw[i].Direction + '</td> <td>' + JSONRaw[i].LastStop + '</td> <td> ' + JSONRaw[i].ETAMinutes + ' </td> </tr>');
}
the undefined rows disappear.. See the image
I apologize for my stupidity but I have never faced any such problem before. What could be the reason??
EDIT:
The array is perfectly fine. And there are no holes in it. Another observation is that the undefined properties appear only at the bottom of my grid. I think it is processing two extra indexes more than the array length.
EDIT-2
My console.log showed me the following elements in my array.
http://i.imgur.com/rI5TjdK.jpg
I've declared the prototypes in my Master page.
Array.prototype.inArray = function (comparer) {
for (var i = 0; i < this.length; i++) {
if (comparer(this[i])) return true;
}
return false;
};
Array.prototype.pushIfNotExist = function (element, comparer) {
if (!this.inArray(comparer)) {
this.unshift(element);
}
};
Is it increasing the array length. ? ?
Using for (var property in array) will cause array to be iterated over as an object, traversing the object prototype chain and ultimately performing slower than an index-based for loop. for (... in ...) is not guaranteed to return the object properties in sequential order, as one might expect.
Basically, the length of an array is the total number of the elements which is contained by all the dimensions of that array. Syntax: public int Length { get; } Property Value: This property returns the total number of elements in all the dimensions of the Array.
The JavaScript array length property states the number of items in an array. To find the length of an array, reference the object array_name. length. The length property returns an integer.
JavaScript's for-in
and C#'s foreach
are quite different things. Don't use for-in
to loop through arrays unless you use the appropriate safeguards, that's not what it's for. It loops through enumerable properties of objects, not array indexes or entries.
Either use the necessary safeguards, or use a generic for
like your second example, or (if you can rely on having it) use Array#forEach
.
More in this SO answer and on my blog.
Separately: Be sure you're declaring i
somewhere (you don't seem to be in your first example). If you don't, you fall prey to The Horror of Implicit Globals.
I've declared the prototypes in my Master page.
Array.prototype.inArray = function ...
This is why you don't use for-in
for looping through arrays without safeguards. Again, see the links above. Your for-in
loop will loop through the array index properties ("0"
, "1"
, and so on -- yes, they're really strings) and also the names of the functions you've added to the Array.prototype
(inArray
and such).
There are a few ways those two loops are different.
(1) The first should be for (var i in JSONRaw)
. Setting the property i
on window
may have non-obvious consequences.
(2) JSONRaw
is not an array, but an object which happens to have a ill-defined length
property.
(3) There have been other properties set on JSONRaw
that appear in the the first loop.
(4) The array could have "holes". For example, the array below has a hole at index 1
.
var a = [];
a[0] = 0;
a[2] = 2;
The first way would omit the 1
. The second way would include the index 1
(with a[1]
being undefined
).
A similar situation can happen as follows:
var a = [0, 1];
a.length = 3;
Personally, I suspect (3).
Doing a console.log
in the first loop may reveal the problem.
EDIT: From your debugging output, it seems (3) was the culprit. inArray
and pushIfNotExist
are keys on all arrays, so the first for
loop iterated over them.
If you are going to to use the first loop, you have three options:
(1) Don't add those functions on Array.prototype
. Adding to built-in prototypes is generally discouraged.
(2) Use Object.defineProperty
(won't work in IE8):
Object.defineProperty(Array.prototype, 'inArray', {
enumerable: false, //this makes it not show up in the for loop
value: function (comparer) {
for (var i = 0; i < this.length; i++) {
if (comparer(this[i])) return true;
}
return false;
}
});
(3) Check to see if the key was defined on the prototype.
for(var i in JSONRaw) {
if(JSONRaw.hasOwnProperty(i)) {
//do something
}
}
Though most people just use the second loop because of the potential pitfalls (and faster performance).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With