Checking if an object is array-like

arrays, javascript

Solution

As best I've found in my research on this topic, you have only a couple choices:

You can look only at the `.length` property and accept any object that seems to have an appropriate `.length` property that isn't any other things you know you should eliminate (like a function).

You can check for specific array-like objects (`HTMLCollection`, `nodeList`) and bias in favor of them.

Here are two options for the first method - one that doesn't accept a zero length and one that does (these incorporate suggestions by gilly3 and things we see in jQuery's similar function):

// see if it looks and smells like an iterable object, but don't accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          item.hasOwnProperty("length") && 
          typeof item.length === "number" && 
          item.length > 0 && 
          (item.length - 1) in item
        )
    );
}

This, of course, reports `false` for items with `.length === 0`, If you want to allow `.length === 0`, then the logic can be made to include that case too.

// see if it looks and smells like an iterable object, and do accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          typeof (item.length) === "number" && 
          (item.length === 0 ||
             (item.length > 0 && 
             (item.length - 1) in item)
          )
        )
    );
}

Some test cases: http://jsfiddle.net/jfriend00/3brjc/

2) After checking to see that it's not an actual array, you can code to check for specific kinds of array-like objects (e.g. `nodeList`, `HTMLCollection`).

For example, here's a method I use when I want to make sure I include nodeList and HTMLCollection array-like objects:

// assumes Array.isArray or a polyfill is available
function canAccessAsArray(item) {
    if (Array.isArray(item)) {
        return true;
    }
    // modern browser such as IE9 / firefox / chrome etc.
    var result = Object.prototype.toString.call(item);
    if (result === "[object HTMLCollection]" || result === "[object NodeList]") {
        return true;
    }
    //ie 6/7/8
    if (typeof item !== "object" || !item.hasOwnProperty("length") || item.length < 0) {
        return false;
    }
    // a false positive on an empty pseudo-array is OK because there won't be anything
    // to iterate so we allow anything with .length === 0 to pass the test
    if (item.length === 0) {
        return true;
    } else if (item[0] && item[0].nodeType) {
        return true;
    }
    return false;        
}

Problem

Is there a way to check if an object is "array-like", like for these types of objects: - Arrays (duh) - Typed Arrays (Uint8Array, etc), these will return false when `Array.isArray` is used - arguments object - NodeLists* - There are a few other ones that I can't think of off-hand I suppose you could check for the presence of a `.length` property, but non-array-like objects can contain the `.length` property. I guess the thing these all share in common is the array accessor.

Original source