Writing code and had to check if an array was empty, however a suggestion was raised with the code I was using.

arr.length === 0

The suggestion was to use the ember built in library, isEmpty, had me thinking, because I’ve written is-really-empty npm package as more of a joke againt the ember isEmpty function. It mostly just checks the length property anyway.

// from ember.js/packages/@ember/-internals/metal/lib/is_empty.ts
  if (typeof obj.length === 'number' && objectType !== 'function') {
    return !obj.length;
  }

  if (objectType === 'object') {
    let length = get(obj, 'length');
    if (typeof length === 'number') {
      return !length;
    }
  }

Which, there are many ways to check if an array is empty

The simple boys

const arr = [];

arr.length === 0 // true
arr.length <= 0 // true
Boolean(arr.length) // true or if(arr.length)

The weird boy

arr == !arr // true
// this will also return true for arr = [0]

the big boy

(arr => {
 for(var a in arr) {
   return false;
 }
 return true;
})(arr) // true

However, lets take a look at what the ember helper tests, this can give us an idea of the type of objects we need.

assert.equal(true, isEmpty(null), 'for null');
assert.equal(true, isEmpty(undefined), 'for undefined');
assert.equal(true, isEmpty(''), 'for an empty String');
assert.equal(false, isEmpty('  '), 'for a whitespace String');
assert.equal(false, isEmpty('\n\t'), 'for another whitespace String');
assert.equal(false, isEmpty(true), 'for true');
assert.equal(false, isEmpty(false), 'for false');
assert.equal(false, isEmpty(string), 'for a String');
assert.equal(false, isEmpty(fn), 'for a Function');
assert.equal(false, isEmpty(0), 'for 0');
assert.equal(true, isEmpty([]), 'for an empty Array');
assert.equal(false, isEmpty({}), 'for an empty Object');
assert.equal(true, isEmpty(object), "for an Object that has zero 'length'");

So lets run down what arr.length === 0 will return if passed each of these


null // TypeError: Cannot read property 'length' of null - BAD
undefined // TypeError: Cannot read property 'length' of undefined - BAD
'' // true - GOOD
'  ' // false - GOOD
'\n\t' // false - GOOD
true // false - GOOD
false // false - GOOD
'string' // false - GOOD
function() {}; // true (depends on parameters the function accepts) - BAD
0 // false - GOOD
[] // true - GOOD
{} // false - GOOD (sorta)
{ length: 0 } // true - GOOD

Some of these return the wrong answer, some of these throw exceptions, the array I was checking against was indeed coming from my code, but not the same file and could potentially be changed by another programmer to behave differently without me knowing. These are just for the examples that are being tested.

Array like objects

Objects like arguments in a function are not arrays, we can turn them into arrays and use any of the above methods using [...arguemtns]. But this will fail if it is not an “Array-like” object.

Getting around this, which also works for Sets or Maps.

// we need to check if this is a proper iterable
if( typeof arguments[Symbol.iterator] === 'function' ) {
  // then we use of instead of in because the previous one technically will return false
  // if you set the length property
  for (var a in arguments) {
    return false;
  }
  return true;
}

Framework specific arrays

Frameworks like Ember and Angular have their own versions of arrays, sometimes they are extensions and the previous methods will work, other times they are brand new objects, if this is the case there usually is a library method isEmpty or something similar, which always works on JS arrays and array like objects.

Beacuse there is a difference and often when using someone elses code we may not know the exact type of an array, a library seeming as trivial as isEmpty is actually quite useful in this situation.