6

I tried this way, but it returns me the wrong count:

myArr = [];
myArr[666] = 'hello there';
console.log(myArr.length); // returns me 667

It should be 1. How can I have the good count in the fastest way?

RogerWilgo
  • 77
  • 2

4 Answers4

17

It should be 1.

No, it should be 667, because that's how length is defined for standard arrays in JavaScript, which aren't really arrays at all. Arrays in JavaScript are inherently sparse, meaning they can have holes in them (indexes where no value of any kind is stored).

How can I have the good count in the fastest way?

The only way is by looping. For instance:

var count = 0;
myArr.forEach(function() {
    ++count;
});
console.log(count);

...or via reduce as in maček's answer. forEach, reduce, and other such only visit entries that actually exist, so they skip the holes in sparse arrays.


I should mention that since IE8 still (sigh) has a fair bit of market share, you need to shim forEach and reduce and such on it if you want to use them. (Which is easy.)

To do this without any shimming, and only count actual index properties (not other kinds of properties), then loosely:

var count = 0, name;
for (name in myArr) {
    if (myArr.hasOwnProperty(name) && /^[0-9]+$/.test(name)) {
        ++count;
    }
}

That uses a slightly loose definition of what an index property name is; if you want a robust one, see the "Use for-in correctly" part of this answer.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    we're on the same page, but a `reduce` is probably a little cleaner – maček Aug 07 '15 at 16:31
  • No prob. +1 for providing extra education. – maček Aug 07 '15 at 16:35
  • If you want a fast way `reduce` is not an option – David Gomez Aug 07 '15 at 17:47
  • @DaveGomez: `reduce` isn't slow. If you're worried about the cost of the function calls, [don't be](http://blog.niftysnippets.org/2012/02/foreach-and-runtime-cost.html). – T.J. Crowder Aug 07 '15 at 17:49
  • It's slow and complicated, why don't just use `filter` and strip out all the `undefined` values? Also the `forEach` solution you post is simply horrible for something as simple as this, nothing to say about the `for in` "solution". – David Gomez Aug 07 '15 at 17:55
  • 1
    @DaveGomez: Repeating "it's slow" doesn't make it true. `filter`'s not going to be any faster -- my guess would be slower, if anything, on an array of any size, since pushing entries into an array is slower than adding one to a value. Also causes more memory-churn (the temporary array). – T.J. Crowder Aug 07 '15 at 17:59
  • Is true, filter is not gonna be faster but it is a lot cleaner and easier to use. Anyway, this is not even a good way to use JavaScript Arrays. – David Gomez Aug 07 '15 at 18:25
  • `filter()` is not just slower, it's a ridiculous tool to reach for given the requirements. It generates a new array... and you don't need one. – canon Aug 07 '15 at 19:45
8

You can count the number of non-undefined properties using a reduce

var length = myArr.reduce(function(len) { return len+1; }, 0);
console.log(length); // 1
maček
  • 76,434
  • 37
  • 167
  • 198
  • I wonder what [DaveGomez](http://stackoverflow.com/users/1650483/dave-gomez) finds unreadable about this. This is perfect. – canon Aug 07 '15 at 20:03
  • 1
    @canon yeah you gotta take everything with a grain of salt on here. Some people don't know what they're talking about. I don't claim to know everything, but I try to avoid providing answers that lack a degree of certainty. – maček Aug 08 '15 at 04:27
5

Because arrays are indexed by 0. [0...666] is 667 items. If you actually want a count of how many items you have, an array might not be the best solution.

Tripp Kinetics
  • 5,178
  • 2
  • 23
  • 37
4

You can use Object.keys().

Object.keys(myArr).length;

From the documentation:

Object.keys returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

This function is compatible with IE9+. The documentation also contains a funtion that can be added for compatibility in all browsers:

if (!Object.keys) Object.keys = function(o) {
  if (o !== Object(o))
    throw new TypeError('Object.keys called on a non-object');
  var k=[],p;
  for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
  return k;
}
John Bupit
  • 10,406
  • 8
  • 39
  • 75