Skip to main content
  1. About
  2. For Teams
Asked
Viewed 154 times
0

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?

10
  • 2
    in forEach passed value, in for loop over index
    Grundy
    –  Grundy
    2015-10-29 09:08:07 +00:00
    Commented Oct 29, 2015 at 9:08
  • @Grundy thank you so much for the reply. If you don't mind could you explain whats happening in this piece of code counts[x]= (counts[x] || 0)+1 How does this code detect the reoccurrence of elements is something I don't understand. I assume to detect repeating elements one would use something like if/else statements.
    Skywalker
    –  Skywalker
    2015-10-29 09:11:21 +00:00
    Commented Oct 29, 2015 at 9:11
  • 1
    @LorenzovonMatterhorn: That's not the question you asked. If you want to ask that question, post it separately. Alexander has answered the question you actually asked (why do the loops behave differently).
    T.J. Crowder
    –  T.J. Crowder
    2015-10-29 09:12:22 +00:00
    Commented Oct 29, 2015 at 9:12
  • count[x]: try get property x from count object, then check it (counts[x] || 0) - this return 0 if counts[x] is falsey, and update value in counts[x] with incremented value.
    Grundy
    –  Grundy
    2015-10-29 09:14:02 +00:00
    Commented Oct 29, 2015 at 9:14
  • @T.J.Crowder I doubt any community on stack exchange will allow me to ask a question on topics like "explain this piece of code". If there is such community then can you please let me know.
    Skywalker
    –  Skywalker
    2015-10-29 09:15:32 +00:00
    Commented Oct 29, 2015 at 9:15

2 Answers 2

3

You've asked two different questions:

  • Why do you get different results using for and Array#forEach?

  • How does counts[x]= (counts[x] || 0)+1 work?

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.).

Sign up to request clarification or add additional context in comments.

1 Comment

Amazing. Thank you so much for this! I really understood this! Thank you again.
2

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);

Comments

Your Answer

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.