Neues Node.js-Buch
Alle Artikel

Create a generic function to convert array-like objects into arrays

Problem

You want to create a generic function to convert array-like objects into arrays (see also recipe 2).

Array-like objects
An array-like object is an object, which behaves like an array, but which isn't one. Array-like objects have a property length and elements can be accessed using indexes, but they typically don't have methods like forEach() etc. Two popular examples for array-like objects are instances of NodeList and the arguments object.

Ingredients

  • the Array prototype
  • the slice() method
  • the call() method
  • the typeof operator

Directions

  1. Given: an array-like object that should be converted.

    function someFunction() {
      var arrayLike = arguments;           // An Array-like object
      var array = toArray(arrayLike);      // Our goal: convert it to an array ...
      array.forEach(function(argument) {   // ... so that we can apply array methods.
        console.log(argument);
      });
    }
    someFunction(2,3,4,5,6,7);
  2. Create a function toArray() that accepts an object.

    function toArray(object) {
    }
  3. Now we need to check if the object is array-like. For that get the length property of the object …

    function toArray(object) {
      var length = object.length;
    }
  4. … and check if the value associated with the length property is a number and not less than 0.

    function toArray(object) {
      var length = object.length;
      var isArrayLike = typeof length == 'number' && length >= 0;
    }
  5. Optionally, but recommended: extract this code into a separate function isArrayLike().

    function isArrayLike(object) {
      var length = object.length;
      return typeof length == 'number' && length >= 0;
    }
    function toArray(object) {
      if(isArrayLike(object)) {
        ...
      }
    }
  6. Back in the toArray() method: borrow the slice() method to convert the object into an array.

    function isArrayLike(object) {
      var length = object.length;
      return typeof length == 'number' && length >= 0;
    }
    function toArray(object) {
      if(isArrayLike(object)) {
        return Array.prototype.slice.call(object);
      }
    }
  7. Before checking if the object is array-like it makes sense to check if the object is already an array (using Array.isArray()).

    function isArrayLike(object) {
      var length = object.length;
      return typeof length == 'number' && length >= 0;
    }
    function toArray(object) {
      if(Array.isArray(object)) {
        return object;
      } else if(isArrayLike(object)) {
        return Array.prototype.slice.call(object);
      }
    }
  8. If the object is neither an array nor array-like: throw an error.

    function isArrayLike(object) {
      var length = object.length;
      return typeof length == 'number' && length >= 0;
    }
    function toArray(object) {
      if(Array.isArray(object)) {
        return object;
      } else if(isArrayLike(object)) {
        return Array.prototype.slice.call(object);
      } else {
        throw new TypeError('Argument could not be converted.');
      }
    }
  9. Voilá, a tasteful helper function for converting array-like objects into arrays (and one for testing if an object is array-like).

    function isArrayLike(object) {
      var length = object.length;
      return typeof length == 'number' && length >= 0;
    }
    function toArray(object) {
      if(Array.isArray(object)) {
        return object;
      } else if(isArrayLike(object)) {
        return Array.prototype.slice.call(object);
      } else {
        throw new TypeError('Argument could not be converted.');
      }
    }
    function someFunction() {
      var arrayLike = arguments;           // Array-like object
      var array = toArray(arrayLike);      // Convert to array
      array.forEach(function(argument) {   // Apply array method
        console.log(argument);
      });
    }
    someFunction(2,3,4,5,6,7);

Alternative recipes

  • Instead of using the arguments object use rest parameters (see also recipe 2).