I realize this is not really directly a jQuery problem, as my example
will bear out, but I think it's a question the development team might
want to consider.
If I am iterating over a given array and wish to dynamically remove
elements from that array, I would expect to be able to do that. For
example:
var arr = [1,2,3,4,5];
for ( var j=0; j<arr.length; j++ ) {
if (arr[j] == "3") arr.splice(j,1);
}
This would make a new array in the arr variable with the elements
[1,2,4,5]. This is all great for a simple array, but what if I'm
iterating over a complex type, like a hash, where an each is more
elegant or preferred?
var hash = { 'one':1, 'two':2, 'three':3, 'four':4, 'five':5 };
var i=0;
for ( var myvalue in hash ) {
if (myvalue == "3") hash.splice(i,1);
i++;
}
This will fail with a TypeError or a RangeError depending on the
browser and version. The length of the hash variable is not updated
correctly, causing the iterator to iterate too far.
It appears that jQuery uses the later of these examples internally.
At the bottom of this is a copy of my test case to show what I found.
The question is: is this behavior considered acceptable? I realize
that if jQuery is wrapping the ES3 for each pattern, there's not much
you can do to change the failure I'm describing. However, is that
acceptable? What are the pros and cons?
Thanks for listening.
John Hollingsworth
-------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/
TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1" />
<title>jQuery.each versus core for each</title>
<script type="text/javascript" src="http://jquery.com/src/jquery-
latest.js"></script>
<script type="text/javascript">
var o = new Array();
$(document).ready(function(){
var arr;
$.each([1,2,3,4,5], function(i,myvalue) {
o[o.length] = "i: " + i + "; " + myvalue;
});
$("#output").html(o.join("<br />\n"));
try {
o.length = 0;
$.each([1,2,3,4,5], function(i,myvalue) {
if (myvalue == "3") this[i-1].remove();
o[o.length] = "i: " + i + "; " + myvalue;
});
} catch ( e ) {
o[o.length] = "-error- " + e.name + "; " + e.description;
}
$("#output1").html(o.join("<br />\n"));
try {
o.length = 0;
arr = [1,2,3,4,5];
var i = 0;
for ( var myvalue in arr ) {
if (myvalue == "3") arr[i].remove();
o[o.length] = "i: " + i + "; " + myvalue;
i++;
}
} catch ( e ) {
o[o.length] = "-error- " + e.name + "; " + e.description;
}
$("#output2").html(o.join("<br />\n"));
try {
o.length = 0;
arr = [1,2,3,4,5];
$.each(arr, function(i,myvalue) {
if (myvalue == "3") this[i-1].remove();
o[o.length] = "i: " + i + "; " + myvalue;
});
} catch ( e ) {
o[o.length] = "-error- " + e.name + "; " + e.description;
}
$("#output3").html(o.join("<br />\n"));
try {
o.length = 0;
arr = [1,2,3,4,5];
i = 0;
for ( var myvalue in arr ) {
if (myvalue == "3") arr.splice(i,1);
o[o.length] = "i: " + i + "; " + myvalue;
i++;
}
} catch ( e ) {
o[o.length] = "-error- " + e.name + "; " + e.description;
}
$("#output4").html(o.join("<br />\n"));
try {
o.length = 0;
arr = [1,2,3,4,5];
for ( var j=0; j<arr.length; j++ ) {
if (arr[j] == "3") arr.splice(j,1);
o[o.length] = "j: " + j + "; " + arr[j];
}
} catch ( e ) {
o[o.length] = "-error- " + e.name + "; " + e.description;
}
$("#output5").html(o.join("<br />\n"));
});
</script>
</head>
<body>
Results from [1,2,3,4,5]:
<span id="output"></span>
Results from [1,2,3,4,5] removing value 3 with $.remove with
$.each:
<span id="output1"></span>
Results from [1,2,3,4,5] removing value 3 with $.remove and
traditional for each pattern:
<span id="output2"></span>
Results from [1,2,3,4,5] removing value 3 with array.splice and for
each pattern:
<span id="output3"></span>
Results from [1,2,3,4,5] removing value 3 with array.splice and for
each pattern:
<span id="output4"></span>
Results from [1,2,3,4,5] removing value 3 with array.splice and
regular for:
<span id="output5"></span>
</body>
</html>