I was searching google to find a function to be able to detect the reoccurrence of elements in array. I found the following solution:
var arr = ["dog","dog","cat","lion","dog","cat" ]
arr.forEach(function(x) {
counts[x]= (counts[x] || 0)+1
});
console.log(counts);
The output of the above is:
Object {dog: 3, cat: 2, lion: 1}
While when using a simple FOR loop:
for(var x=0; x<arr.length; x++){
counts[x]= (counts[x] || 0)+1
}
console.log(counts);
The output of the above is:
Object {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1}
Issues:
Firstly I don't completly understand what is happening here:
counts[x]= (counts[x] || 0)+1
Secondly how does in a ForEach loop it is able to detect reoccurrence of elements? Is this already built in?
In the below code the property names dog, cat and lion only appears once.
Object {dog: 3, cat: 2, lion: 1}
While here the property name appears separately for each item in the array.
Object {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1}
Maybe I don't understand how the ForEach loop works but why is the behaviour of both loops different? The for each loop seems to detect repeating property names?
2 Answers 2
You've asked two different questions:
Why do you get different results using
forandArray#forEach?How does
counts[x]= (counts[x] || 0)+1work?
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.).
1 Comment
First argument in forEach callback is value not index
var arr = ["dog","dog","cat","lion","dog","cat" ]
var forEachCounts = {};
arr.forEach(function(x) {
forEachCounts[x]= (forEachCounts[x] || 0)+1
});
console.log(forEachCounts);
var forCount = {};
for(var x = 0; x < arr.length; x++){
forCount[arr[x]] = (forCount[arr[x]] || 0)+1
}
console.log(forCount);
forEachpassed value, inforloop over indexcount[x]: try get propertyxfromcountobject, then check it(counts[x] || 0)- this return0ifcounts[x]is falsey, and update value incounts[x]with incremented value.