You've asked two different questions:
Why do you get different results using for
and Array#forEach
?
Alexander has answered the first one, but just for completeness, it's because x
in your for
loop is the key (index) for that loop iteration (0
, 1
, 2
, and so on); but x
in your Array#forEach
callback is the value for that loop iteration ("dog"
, "dog"
, "cat"
, and so on).
Assuming a non-sparse array, the for
equivalent of
theArray.forEach(function(value) {
// ...do something with value
});
is
var index, value;
for (index = 0; index < theArray.length; ++index) {
value = theArray[index];
// ...do something with value
}
...except of course with the for
example we're declaring two variables we don't declare with the forEach
example.
For what you're doing, you want the value, not the index, so if you wanted to use a for
loop, it should be:
var x;
for(var index=0; index<arr.length; index++){
x = arr[index];
counts[x]= (counts[x] || 0)+1
}
console.log(counts);
How does counts[x]= (counts[x] || 0)+1
work?
You haven't shown how counts
is initialized, but I'm assuming it's
var counts = {};
or
var counts = []; // (This would mostly be wrong, but it works)
So that means, if x
is "dog"
, counts[x]
will be undefined
, because counts
doesn't start out having a property called dog
in it. Then you do this:
counts[x]= (counts[x] || 0)+1
...which is
counts[x]= (counts["dog"] || 0)+1
...which is
counts[x]= (undefined || 0)+1
JavaScript's ||
operator is curiously powerful: It evaluates the left-hand side and, if that's "truthy", takes the left-hand side as its result; if the left-hand side is "falsey", the operator evaluates the right-hand side and takes that as its result. The "truthy" values are all values that aren't "falsey"; the "falsey" values are undefined
, null
, 0
, ""
, NaN
, and of course, false
.
So that means undefined || 0
is 0
, because undefined
is falsey, and we have:
counts[x]= (0)+1
...which is
counts[x]= 1
So now, counts[x]
(where x
is "dog"
) contains 1
.
The next time you do that for x = "dog"
, you have
counts[x]= (counts[x] || 0)+1
...which is
counts[x]= (counts["dog"] || 0)+1
...which is
counts[x]= (1 || 0)+1
Since 1
is truthy, 1 || 0
is 1
, so we have:
counts[x]= (1)+1
...which is
counts[x]= 2
This continues, and so it counts up the number of times you see "dog"
in the array (and also "cat"
, "lion"
, etc.).
forEach
passed value, infor
loop over indexcount[x]
: try get propertyx
fromcount
object, then check it(counts[x] || 0)
- this return0
ifcounts[x]
is falsey, and update value incounts[x]
with incremented value.