Reputation: 124646
For some inexplicable reason, I cannot convert this loop into a more natural for (var player in fizzPlayers) { ... }
loop:
for (var i = 0; i < fizzPlayers.length; i++) {
var player = fizzPlayers[i];
var val = parseInt(player.value);
if (val != 0) {
active.push(player);
actfact.push(parseInt(player.value));
actname.push(capitaliseFirstLetter(player.id));
}
}
I want to write this the more natural for-each way, like this:
for (var player in fizzPlayers) {
var val = parseInt(player.value);
if (val != 0) {
active.push(player);
actfact.push(parseInt(player.value));
actname.push(capitaliseFirstLetter(player.id));
}
}
But it doesn't work, as you can see in the runnable code snippet at the bottom.
For debugging, I inserted this code right before the loop:
// prints undefined... but why?
for (var player in fizzPlayers) {
console.log(player.value);
}
It prints undefined
for the 5 players. Why? There is a similar loop earlier in the program where the for (var player in fizzPlayers) { ... }
loop is working just fine.
Why is this happening? What am I missing?
var fizzLoaded = false;
var fizzDiv, fizzFrom, fizzTo, fizzPlayers;
function fizzLoad() {
if (fizzLoaded) {
return;
}
fizzLoaded = true;
var fizzForm = document.getElementById('fizzbuzz');
fizzFrom = document.getElementById('rangeFrom');
fizzTo = document.getElementById('rangeTo');
fizzPlayers = [
document.getElementById('frodo'),
document.getElementById('sam'),
document.getElementById('merry'),
document.getElementById('pippin'),
document.getElementById('bilbo')
];
fizzDiv = document.getElementById('fizzOut');
}
function restrictRange() {
var rFrom = parseInt(fizzFrom.value);
var rTo = parseInt(fizzTo.value);
fizzTo.min = rFrom;
fizzFrom.max = rTo;
}
function validateValues() {
var rFrom = parseInt(fizzFrom.value);
var rTo = parseInt(fizzTo.value);
if (rTo < rFrom) {
alert("Illegal range from " + rFrom + " to " + rTo);
return false;
}
for (var player in fizzPlayers) {
var val = parseInt(player.value);
if (val < 0 || val > 100) {
alert("Illegal value " + val + " for player " + player.id);
return false;
}
}
return true;
}
function capitaliseFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function fizzing() {
fizzLoad();
restrictRange();
if (!validateValues()) {
fizzDiv.innerHTML = "Illegal inputs";
return;
}
var table = "";
var rFrom = parseInt(fizzFrom.value);
var rTo = parseInt(fizzTo.value);
var active = [];
var actfact = [];
var actname = [];
// prints undefined... but why?
for (var player in fizzPlayers) {
console.log(player.value);
}
for (var player in fizzPlayers) {
var val = parseInt(player.value);
if (val != 0) {
active.push(player);
actfact.push(parseInt(player.value));
actname.push(capitaliseFirstLetter(player.id));
}
}
table += "<table>\n";
table += " <tr><th>Value</th><th>Message</th></tr>\n";
for (var i = rFrom; i <= rTo; i++) {
var msg = "";
for (var p = 0; p < active.length; p++) {
if (i % actfact[p] == 0) {
msg += actname[p];
}
}
if (msg == "") {
msg = "" + i;
}
table += " <tr><td>" + i + "</td><td>" + msg + "</td></tr>\n";
}
table += "</table>\n";
fizzDiv.innerHTML = table;
}
h1 {
clear: left;
}
hr {
clear: left;
}
label {
display: inline-block;
float: left;
clear: left;
width: 150px;
text-align: left;
}
input {
display: inline-block;
float: right;
text-align: right;
padding-left:10px;
width: 50px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>FizzBuzz</title>
<link rel="stylesheet" href="fizzbuzz.css">
<script src="fizzbuzz.js" type="text/javascript"></script>
</head>
<body>
<h1>Config</h1>
<form id="fizzbuzz">
<fieldset id="fizzControl" oninput="fizzing();">
<label>Range From<input id="rangeFrom" type="number" min="1" max="100" value="1" required></label>
<label>Range To<input id="rangeTo" type="number" min="1" max="1024" value="100" required></label>
<div id="players" >
<label>Frodo<input id="frodo" type="number" min="0" max="100" value="3" required></label>
<label>Sam<input id="sam" type="number" min="0" max="100" value="5" required></label>
<label>Merry<input id="merry" type="number" min="0" max="100" value="0" required></label>
<label>Pippin<input id="pippin" type="number" min="0" max="100" value="0" required></label>
<label>Bilbo<input id="bilbo" type="number" min="0" max="100" value="0" required></label>
</div>
</fieldset>
</form>
<hr>
<h1>Output</h1>
<div id="fizzOut" >Change a value to get output (a snippet thing)</div>
<script>fizzing();</script>
</body>
</html>
Upvotes: 4
Views: 1095
Reputation: 229321
Check what is actually being assigned to player
:
> fizzPlayers = ["jim", "bob", "joe"]
> for (var player in fizzPlayers) {
console.log(player);
}
0
1
2
Iterating through an object gives you the keys. The keys of an array are the indices. In this case you are better off using a regular for loop rather than this for-each loop, since if you have assigned any other properties to the array (like fizzPlayers.barg = 40;
) the for-each loop will give you those property names as well
However, if you are using jQuery, you can use $.each
:
> $.each(fizzPlayers, function (index, player) {
console.log(index + ": " + player);
});
0: jim
1: bob
2: joe
Note that the callback takes two parameters: the index, and the value.
Upvotes: 4
Reputation: 30330
The idiomatic way to write a loop that iterates over Array elements is forEach
. As comments have noted, for... in
is for Object keys, not Array elements.
fizzPlayers.forEach(function(player) {
var val = parseInt(player.value);
if (val != 0) {
active.push(player);
actfact.push(parseInt(player.value));
actname.push(capitaliseFirstLetter(player.id));
}
});
You can Array.prototype.forEach
in all modern browsers (IE9 +).
Upvotes: 9
Reputation: 8021
The for-each loop assigns the index
// prints undefined... but why?
for (var playerIndex in fizzPlayers) {
console.log(fizzPlayers[playerIndex].value);
}
You could consider a convenience library like lo-dash for utility functions.
Upvotes: 1
Reputation: 7582
You need
for (var p in fizzPlayers) {
console.log(fizzPlayers[p].value);
}
This is a common annoyance. An ECMAScript 6 proposal has
for (var player of fizzPlayers) {
console.log(player.value);
}
Upvotes: 2
Reputation: 53958
A for in
loop in JavaScript
runs through the enumerable properties of an object. Hence, you can't convert it to a classic for
loop. More formally, from the MDN:
A for...in loop only iterates over enumerable properties. Objects created from built–in constructors like Array and Object have inherited non–enumerable properties from Object.prototype and String.prototype, such as String's indexOf() method or Object's toString() method. The loop will iterate over all enumerable properties of the object itself and those the object inherits from its constructor's prototype (properties closer to the object in the prototype chain override prototypes' properties).
Upvotes: 1
Reputation: 49803
In Javascript, the for-each
loop assigns the index to the control variable, so you would need to use fizzPlayers[player]
instead of just player
. You might not like it (I often don't), but that's how it works.
Upvotes: 3