0

THE ISSUE

I'm not really a beginner, but this might be a beginner question. I'm trying to increment a count property of an ES6 JavaScript object. The two most successful (as in they give me wrong info, but no error) versions of this minimal code example either return "NaN" or "1" for each fruit's count property. This seems like it should work, but apparently incrementing an numeric object property is not an intuitive concept.

So, here is what I'm trying to do:

Create object of fruits as keys and how many times they appear. End object would look like this:

fruitsObj[fruit].name
fruitsObj[fruit].count

So for "apple":

fruitsObj.apple.name // apple
fruitsObj.apple.count // 4

But, the output I receive is:

{apple: {…}, pear: {…}, banana: {…}, orange: {…}, kumquat: {…}}
apple:{name: "apple", count: NaN}
banana:{name: "banana", count: NaN}
kumquat:{name: "kumquat", count: NaN}
orange:{name: "orange", count: NaN}
pear:{name: "pear", count: NaN}
__proto__: Object

In Version 2 I tried doing existence and type checking, to no avail. I get "1" as the only value for each count:

{apple: {…}, pear: {…}, banana: {…}, orange: {…}, kumquat: {…}}
apple:{name: "apple", count: 1}
banana:{name: "banana", count: 1}
kumquat:{name: "kumquat", count: 1}
orange:{name: "orange", count: 1}
pear:{name: "pear", count: 1}
__proto__: Object

I'm not sure what I'm doing wrong here.

MY CODE

Version 1:

// Declare an array of fruits
var fruitsArr = ['apple', 'pear', 'apple', 'banana', 'orange', 'kumquat', 'orange', 'apple', 'apple', 'orange'];

var fruitsObj = {};

// Loop through each fruit storing name and count.
fruitsArr.forEach(function(fruit, i) {
 
 // Create each named-fruit property in object as a sub-object.
 fruitsObj[fruit] = {};
 
 fruitsObj[fruit].name = fruit;
 
 // FAILS with NaN!
 fruitsObj[fruit].count = ++fruitsObj[fruit].count;
 
});

console.log(fruitsObj);

Version 2:

// Declare an array of fruits
var fruitsArr = ['apple', 'pear', 'apple', 'banana', 'orange', 'kumquat', 'orange', 'apple', 'apple', 'orange'];

var fruitsObj = {};

// Loop through each fruit storing name and count.
fruitsArr.forEach(function(fruit, i) {
 
 // Create each named-fruit property in object as a sub-object.
 fruitsObj[fruit] = {};
 
 fruitsObj[fruit].name = fruit;

 // if fruit count exists and is greater than zero
 if (fruitsObj[fruit].count != undefined && fruitsObj[fruit].count > 0) {
  // increment it
  fruitsObj[fruit].count++;
 } else {
  // set it to one
  fruitsObj[fruit].count = 1;
 } 
 
});

console.log(fruitsObj);

Links I Consulted

I review a bunch of posts and links that sent me down a rabbit hole. Even so, none of them clearly addressed my issue that I could tell.

MY QUESTIONS

  1. Why is my counter not incrementing, but instead returning 'NaN'?
  2. Is there some special way to declare an object property as a number before assigning a value?
  3. What am I missing in my understanding to make this work? How can I increment my object count properties?
halfer
  • 19,824
  • 17
  • 99
  • 186
  • Initially the `count` property has a value of `undefined` and `undefined + 1` results in `NaN`. The shortest way to deal with this is simply : `fruitsObj[fruit].count = ++(fruitsObj[fruit].count || 0);` – Slim Mar 06 '18 at 12:14
  • you cant increment something when there is nothing to increment:`fruitsObj = {}`. Maybe you should provide us an expected result – Roman Mar 06 '18 at 12:14
  • eeeeeeeeeeeeh you are generating a counter for each fruit and each fruit is inserted once so will always be 1 on the version 2, i don't know if you want a counter or index – LPZadkiel Mar 06 '18 at 12:19
  • I really don't understand what you want to "count" exactly. The amount of certain fruit that you have? please be clear with that – LPZadkiel Mar 06 '18 at 12:22

3 Answers3

3

Initialise the fruit's object with count: 0, when it doesn't exist, and then increment the count by 1 for each fruit with the same name:

// Declare an array of fruits
var fruitsArr = ['apple', 'pear', 'apple', 'banana', 'orange', 'kumquat', 'orange', 'apple', 'apple', 'orange'];

var fruitsObj = {};

// Loop through each fruit storing name and count.
fruitsArr.forEach(function(fruit) {
  // if the object lacks a [fruit] property
  if(!fruitsObj[fruit]) {
    // create a new [fruit] object, with the name and count: 0
    fruitsObj[fruit] = { 
      name: fruit,
      count: 0
    };
  }
 
  // increment the count of the [fruit] object
  fruitsObj[fruit].count++;
});

console.log(fruitsObj);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
3

Why is my counter not incrementing, but instead returning 'NaN'?

Because you are trying to increment an undefined property in the first case. In line

fruitsObj[fruit].count = ++fruitsObj[fruit].count;

fruitsObj[fruit].count is undefined, because you never assigned any number value to it. This problem you corrected in second code block.

What am I missing in my understanding to make this work? How can I increment my object count properties?

The problem with second code is this line

// Create each named-fruit property in object as a sub-object.
    fruitsObj[fruit] = {};

You are assigning a new object in every iteration. So even if this fruit is coming the second time, the new object overrides previous value when you assign a new object. You can eliminate this by checking if this object already exist or not like this

// Declare an array of fruits
    var fruitsArr = ['apple', 'pear', 'apple', 'banana', 'orange', 'kumquat', 'orange', 'apple', 'apple', 'orange'];
    
    var fruitsObj = {};
    
    // Loop through each fruit storing name and count.
    fruitsArr.forEach(function(fruit, i) {
     
     // Create a fruit object only if it does not exist already.
        if (!fruitsObj[fruit]) {
        fruitsObj[fruit] = {};
        }
     
     fruitsObj[fruit].name = fruit;
    
     // if fruit count exists and is greater than zero
     if (fruitsObj[fruit].count != undefined && fruitsObj[fruit].count > 0) 
        {
      // increment it
      fruitsObj[fruit].count++;
     } else {
      // set it to one
      fruitsObj[fruit].count = 1;
     } 
     
    });
    
    console.log(fruitsObj);
Prakash Sharma
  • 15,542
  • 6
  • 30
  • 37
  • 2
    Thanks, Prakash! Although I think Ori's code is better, cleaner and more efficient, you addressed my specific questions and helped me realize why my code wasn't working, so I'm giving you the win. – Eric Hepperle - CodeSlayer2010 Mar 06 '18 at 12:33
1

In your forEach loop, try only these two lines:

if(fruitObj[fruit]) fruitObj[fruit].count+=1;
else fruitObj[fruit] = {name: fruit, count: 1};

This is instantiating or incrementing, depending on previous instantiation. You cannot increment an uninstatiated value, and in your second version you are instantiating new empty object every time (unconditionally).

Alexander
  • 19,906
  • 19
  • 75
  • 162