Array Surprises in JavaScript

· 7 min read Đọc bài này bằng tiếng Việt

Having worked with several programming languages (Java, C, C++, C#, Objective-C, PHP), each has its own charm. But when I encountered JavaScript (JS), I suddenly fell in love — with its sexy and surprising nature. Arrays in JS, for instance, are very special and surprising. In this article, I’ll note down some points that may surprise others as they surprised me.

How do you get the length of an array?

Yesterday while coding, I used the length property to get array length and noticed a discrepancy with the number of elements retrieved, which surprised me. Re-reading JS documentation, I realized there seems to be no property storing the count of actual elements (non-undefined) in it. Or at least I haven’t found one. If any expert can enlighten me, that would be great. Try the following code and you’ll see that the length property equals the largest index plus 1.

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['js'] = 'JavaScript';
console.log(arr.length);

This prints the length of arr as 21 — the largest index (20) plus 1. A bit surprising because I initially thought it would return 3 (the number of non-undefined elements).

Do non-numeric indices affect anything? The answer is: nothing regarding length. You can see in the following example that length does not depend on non-numeric indices.

var arr = [];
arr[-1] = 100;
arr['js'] = 'JavaScript';
arr['me'] = 'Java Lover';
console.log(arr.length);

This always prints 0, proving that non-numeric indices have no effect on the length property.

So the question is: how do you get the exact count of elements in an array?

A simple approach is to iterate and count. But it’s not as simple as we think.

How do you iterate over an array?

Iterating arrays in JS is also very interesting. The usual way is to get length and loop from start to end:

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['me'] = 'Java Lover';
var counter = 0;
for (var i = 0, len = arr.length; i < len; i++) {
  console.log(i + ': ' + arr[i]);
  if (typeof arr[i] != 'undefined') counter++;
}
console.log('The size of array: %d', counter);

The result shows elements with indices 0–9 and 11–19 as undefined, and counter is 2. So counter doesn’t give the right result, and for large arrays (1000 elements, for example), we can’t iterate this way. Let’s try Array.forEach:

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['me'] = 'XXX Lover';
var counter = 0;
arr.forEach(function (ele, i, array) {
  console.log(i + ': ' + ele);
  counter++;
});
console.log('The size of array: %d', counter);

This also doesn’t work — same result as iterating by length. Another way is for in:

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['me'] = 'Java Lover';
var counter = 0;
for (var i in arr) {
  console.log(i + ': ' + arr[i]);
  counter++;
}
console.log('The size of array: %d', counter);

This gives the exact result, printing each element and returning a size of 3.

From this, we can see that we should be careful with the length property and choose the right iteration method. For contiguous data, iterating by length or Array.forEach works fine. But for sparse data and non-numeric indices, use for in.

How to check if a variable is an array?

Try running this code:

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['me'] = 'JavaScript Lover';

console.log("Trust me, man, I'm an Array @@");
console.log('Really, I have to check your DNA.');
console.log('JavaScript checker: arr is ' + typeof arr);

??? You’re an Object, why do you claim to be an Array? No, I’m an array — check with this tool:

var arr = [];
arr[10] = 0;
arr[20] = 'index 20';
arr['me'] = 'JavaScript Lover';

console.log(
  'JavaScript checker: Is arr an array ... ' +
    (Object.prototype.toString.apply(arr) === '[object Array]')
);

Oh, oh, monkey — how can that be? See, I’m of royal descent, so I must hide my true identity. Spotting a pretty girl is easy, but telling a good one from a bad one takes real skill.

To wrap up, here’s a small piece of code for Node.js beginners. The task: fetch the contents of 3 web pages (HTTP) with addresses from command-line arguments asynchronously and print the page contents in the same order as the input arguments. You’ll see its connection to the array topics discussed above.

var http = require('http');
var bl = require('bl');
var data = [];
var counter = 0;

function printData() {
  for (var i = 0; i < 3; i++) console.log(data[i]);
}

function loadData(index) {
  http.get(process.argv[2 + index], function (resp) {
    resp.pipe(
      bl(function (err, buf) {
        if (err) data[index] = err.toString();
        else data[index] = buf.toString();
        counter++;
        if (counter === 3) printData();
      })
    );
  });
}

for (var i = 0; i < 3; i++) loadData(i);

Comments

  1. Loading comments…