Objects don't have a .length property.

A simple solution if you know you don't have to worry about hasOwnProperty checks, would be to do this:

Object.keys(data).length;

If you have to support IE 8 or lower, you'll have to use a loop, instead:

var length= 0;
for(var key in data) {
    if(data.hasOwnProperty(key)){
        length++;
    }
}
Answer from Cerbrus on Stack Overflow
Top answer
1 of 7
15

Note: Array indexes are nothing but properties of Array objects.

Quoting MDN's Relationship between length and numerical properties section,

When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array's length property accordingly.

Quoting ECMA Script 5 Specification of Array Objects,

whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted

So, when you set a value at index 5, JavaScript engine adjusts the length of the Array to 6.


Quoting ECMA Script 5 Specification of Array Objects,

A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 232−1.

So, in your case 2 and 4 are valid indexes but only 2 is defined in the array. You can confirm that like this

arr.hasOwnProperty(2)

The other indexes are not defined in the array yet. So, your array object is called a sparse array object.

So why arr[2] is counted in for..in loop and not arr[4] is not counted?

The for..in enumerates all the valid enumerable properties of the object. In your case, since only 2 is a valid property in the array, it will be counted.

But, when you print arr[4], it prints undefined, because JavaScript will return undefined, if you try to access a property which is not defined in an object. For example,

console.log({}['name']);
// undefined

Similarly, since 4 is not yet defined in the arr, undefined is returned.


While we are on this subject, you might want to read these answers as well,

  • Why doesn't the length of the array change when I add a new property?

  • JavaScript 'in' operator for undefined elements in Arrays

2 of 7
9

There’s a difference between a property that has the value undefined and a property that doesn’t exist, illustrated here using the in operator:

var obj = {
    one: undefined
};

console.log(obj.one === undefined); // true
console.log(obj.two === undefined); // true

console.log('one' in obj); // true
console.log('two' in obj); // false

When you try to get the value of a property that doesn’t exist, you still get undefined, but that doesn’t make it exist.

Finally, to explain the behaviour you see: a for in loop will only loop over keys where that key is in the object (and is enumerable).

length, meanwhile, is just adjusted to be one more than whatever index you assign if that index is greater than or equal to the current length.

Find elsewhere
Top answer
1 of 3
1

The reason is that you're not logging superArray in your fetch promise. If you add the call to console.log() in your last then() call it works. The reason for this is that fetch is executed asynchronously, which means that any additional code that comes after the fetch call is executed long before the fetch has returned anything.

The reason you can see a full log of superArray even when doing it outside the fetch is a special console behaviour.

const URL1 = "https://www.politifact.com/api/statements/truth-o-meter/people/barack-obama/json/?n=50";
const URL2 = "https://www.politifact.com/api/statements/truth-o-meter/people/hillary-clinton/json/?n=210";
const URL3 = "https://www.politifact.com/api/statements/truth-o-meter/people/bernie-s/json/?n=70";
var superArray = [];
fetch("https://cors-anywhere.herokuapp.com/" + URL1)
  .then(results => {
    return results.json();
  })
  .then(data => {
    data = data.filter(function(item) {
      return item.speaker.name_slug == "barack-obama" && item.statement_type.statement_type !== "Flip";
    });
    for (var i = 0; i < 10; i++) {
      superArray.push(data.splice(Math.random() * data.length, 1)[0]);
    }
  })
fetch("https://cors-anywhere.herokuapp.com/" + URL2)
  .then(results => {
    return results.json();
  })
  .then(data => {
    data = data.filter(function(item) {
      return item.speaker.name_slug == "hillary-clinton" && item.statement_type.statement_type !== "Flip";
    });
    for (var i = 0; i < 10; i++) {
      superArray.push(data.splice(Math.random() * data.length, 1)[0]);
    }
  })
fetch("https://cors-anywhere.herokuapp.com/" + URL3)
  .then(results => {
    return results.json();
  })
  .then(data => {
    data = data.filter(function(item) {
      return item.speaker.name_slug == "bernie-s" && item.statement_type.statement_type !== "Flip";
    });
    for (var i = 0; i < 10; i++) {
      superArray.push(data.splice(Math.random() * data.length, 1)[0]);
    }
    console.log(superArray[0]);
  })

2 of 3
0

It's possible to be logged into your console before data is fetched. To make it sure, log that after accomplishing the data ie. inside .then().

fetch(...)
.then(results => {...})
.then(data => {...})
.then(()=> console.log(superArr[0]))

Or, you may use:

superArray.length && console.log(superArray[0]);
🌐
EyeHunts
tutorial.eyehunts.com › home › javascript array length undefined | code
JavaScript array length undefined | Code
June 30, 2023 - Remember that array indices start from 0. const myArray = []; myArray[0] = "First element"; myArray[1] = "Second element"; // ... 3. Overwriting the length property: JavaScript arrays have a built-in length property that represents the number of elements in the array.
Top answer
1 of 4
19

That's because length gives you the next index available in the array.

DOCS

arrayLength

If the only argument passed to the Array constructor is an integer between 0 and 2^32-1 (inclusive), this returns a new JavaScript array with length set to that number.

ECMA Specifications

Because you don't have inserted any element in the other keys than 21, 90, 13, all the remaining indexes contains undefined. DEMO

To get actual number of elements in the array:

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};

var len = 0;

for (var i = 0; i < a.length; i++) {
  if (a[i] !== undefined) {
    len++;
  }
}
document.write(len);

Shorter version

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};


for (var i = 0, len = 0; i < a.length; i++, a[i] !== undefined && len++);


document.write(len);

DEMO

EDIT

If the array contains large number of elements, looping to get its length is not the best choice.

As you've mentioned in the question, Object.keys(arr).length is the best solution in this case, considering that you don't have any properties added on that array. Otherwise, the length will not be what you might be expecting.(Thanks To @RobG)

2 of 4
6

The array in JavaScript is a simple zero-based structure. The array.length returns the n + 1 where n is the maximum index in an array.

That's just how it works - when you assign 90'th element and this array's length is less than 90, it expands an array to 90 and sets the 90-th element's value. All missing values are interpreted as null.

If you try the following code:

var a = [];
a[21] = {};
a[90] = {};
a[13] = {};
console.log(JSON.stringify(a));

You will get the following JSON:

[null,null,null,null,null,null,null,null,null,null,null,null,null,{},null,null,null,null,null,null,null,{},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,{}]

Moreover, array.length is not a readonly value.
If you set a length value less than the current, then the array will be resized:

 var arr = [1,2,3,4,5];
 arr.length = 3;
 console.log(JSON.stringify(arr));
 // [1,2,3]

If you set a length value more than the current, then the array will be expanded as well:

 var arr = [1,2,3];
 arr.length = 5;
 console.log(JSON.stringify(arr));
 // [1,2,3,null,null]

In case you need to assign such values, you can use JS objects.
You can use them as associative array and assign any key-value pairs.

var a = {};
a[21] = 'a';
a[90] = 'b';
a[13] = 'c';
a['stringkey'] = 'd';
a.stringparam = 'e'; // btw, a['stringkey'] and a.stringkey is the same

console.log(JSON.stringify(a)); 
// returns {"13":"c","21":"a","90":"b","stringkey":"d","stringparam":"e"}

console.log(Object.keys(a).length);
// returns 5
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Global_Objects › Array › length
Array: length - JavaScript - MDN Web Docs
When length is set to a bigger value than the current length, the array is extended by adding empty slots, not actual undefined values.