By Tom


2008-10-21 18:04:53 8 Comments

Is there a more efficient way to convert an HTMLCollection to an Array, other than iterating through the contents of said collection and manually pushing each item into an array?

7 comments

@harpo 2008-10-21 18:06:47

var arr = Array.prototype.slice.call( htmlCollection )

will have the same effect using "native" code.

Edit

Since this gets a lot of views, note (per @oriol's comment) that the following more concise expression is effectively equivalent:

var arr = [].slice.call(htmlCollection);

But note per @JussiR's comment, that unlike the "verbose" form, it does create an empty, unused, and indeed unusable array instance in the process. What compilers do about this is outside the programmer's ken.

Edit

Since ECMAScript 2015 (ES 6) there is also Array.from:

var arr = Array.from(htmlCollection);

Edit

ECMAScript 2015 also provides the spread operator, which is functionally equivalent to Array.from (although note that Array.from supports a mapping function as the second argument).

var arr = [...htmlCollection];

I've confirmed that both of the above work on NodeList.

@Heath Borders 2009-02-26 19:47:08

This fails in IE6.

@Oriol 2014-04-24 19:04:04

The shortcut [].slice.call(htmlCollection) also works.

@Erik Reppen 2014-06-10 19:12:48

@ChrisNielsen Yes I was misinformed on that. Sorry for spreading that around. I didn't realize I'd stated that here as well. Deleted the comment to avoid confusion but for context I had read (or misread) somewhere that slicing an HTMLCollection made it behave like both an array and a collection. Totally incorrect.

@ruffin 2015-06-05 19:24:34

Fwiw, the content of Oriol's comment was also mentioned in @AUTO 's answer, below about a month earlier, if you want to spread the karma around a little.

@Chaoix 2015-10-20 17:08:33

The MDN docs for Array.prototype.slice have a nice explanation of when to use this type of conversion and a nice polyfill for Internet Explorer < 9. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

@Nico 2017-01-29 14:20:05

var arr = [].slice.call(htmlCollection); fails in IE 11, but Array.prototype still works

@JussiR 2017-02-08 14:42:06

The [].slice shortcut is not equivalent since it also creates unused empty array instance. Not sure if compilers are able to optimize it away, though.

@Lev Chlumov 2017-08-17 04:05:21

Great. Thank you.

@Frank Conijn 2018-06-17 12:07:00

Array.from, i.e. from, is not supported by IE11.

@forethought 2019-01-27 17:55:15

How does Array.prototype.slice.call( htmlCollection) work? Can anyone here explain?

@harpo 2019-01-28 02:04:50

@forethought, Array.prototype.slice is not normally used this way. Instead, you would call it as a method on an array like [3, 5, 7].slice(). That creates and returns a new Array based on the indexes passed to slice. When you pass no arguments, it copies the whole array. HTML collections don't have Array in their prototype chain, so you can't use the slice method on references to them. However, if you use call() (which is on Function.prototype) to invoke Array.prototype.slice on an array-like object, it accomplishes the same thing. Hope this helps!

@oldboy 2019-07-16 06:05:19

the spread operator did not work, at least not like this: [...htmlColl].forEach((i)=>{//do})

@Shahar Shokrani 2018-07-10 18:40:55

To convert array-like to array in efficient way we can make use of the jQuery makeArray :

makeArray: Convert an array-like object into a true JavaScript array.

Usage:

var domArray = jQuery.makeArray(htmlCollection);

A little extra:

If you do not want to keep reference to the array object (most of the time HTMLCollections are dynamically changes so its better to copy them into another array, This example pay close attention to performance:

var domDataLength = domData.length //Better performance, no need to calculate every iteration the domArray length
var resultArray = new Array(domDataLength) // Since we know the length its improves the performance to declare the result array from the beginning.

for (var i = 0 ; i < domDataLength ; i++) {
    resultArray[i] = domArray[i]; //Since we already declared the resultArray we can not make use of the more expensive push method.
}

What is array-like?

HTMLCollection is an "array-like" object, the array-like objects are similar to array's object but missing a lot of its functionally definition:

Array-like objects look like arrays. They have various numbered elements and a length property. But that’s where the similarity stops. Array-like objects do not have any of Array’s functions, and for-in loops don’t even work!

@Nicholas 2016-08-13 06:35:55

This works in all browsers including earlier IE versions.

var arr = [];
[].push.apply(arr, htmlCollection);

Since jsperf is still down at the moment, here is a jsfiddle that compares the performance of different methods. https://jsfiddle.net/qw9qf48j/

@Shahar Shokrani 2018-07-10 20:26:24

try var args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));

@mido 2016-05-05 04:23:12

not sure if this is the most efficient, but a concise ES6 syntax might be:

let arry = [...htmlCollection] 

Edit: Another one, from Chris_F comment:

let arry = Array.from(htmlCollection)

@Chris_F 2016-06-13 08:14:38

Additionally, ES6 adds Array.from()

@Marcel M. 2017-03-16 13:32:03

Watch out for the first one, there's a subtle bug when transpiling with babel where [...htmlCollection] will return an array with the htmlCollection as it's only element.

@Bobby 2017-07-04 02:43:06

Array spread operator doesn't work on htmlCollection. It is only applicable to NodeList.

@Frank Conijn 2018-06-17 12:08:19

Array.from, i.e. from, is not supported by IE11.

@RedSparr0w 2018-06-26 22:14:58

Benchmark Looks like the spread operator is faster out of these 2.

@Codesmith 2014-03-27 00:42:57

I saw a more concise method of getting Array.prototype methods in general that works just as well. Converting an HTMLCollection object into an Array object is demonstrated below:

[].slice.call( yourHTMLCollectionObject );

And, as mentioned in the comments, for old browsers such as IE7 and earlier, you simply have to use a compatibility function, like:

function toArray(x) {
    for(var i = 0, a = []; i < x.length; i++)
        a.push(x[i]);

    return a
}

I know this is an old question, but I felt the accepted answer was a little incomplete; so I thought I'd throw this out there FWIW.

@Gustavo 2013-05-16 19:35:08

This is my personal solution, based on the information here (this thread):

var Divs = new Array();    
var Elemns = document.getElementsByClassName("divisao");
    try {
        Divs = Elemns.prototype.slice.call(Elemns);
    } catch(e) {
        Divs = $A(Elemns);
    }

Where $A was described by Gareth Davis in his post:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

If browser supports the best way, ok, otherwise will use the cross browser.

@Patrick 2015-04-26 17:52:21

In general, I don't expect try/catch to be an efficient way to manage control flow. You can check if the function exists first, then run either one or the other a bit cheaper.

@RobG 2016-01-04 09:50:48

As with Gareth Davis' answer, this creates new, undefined members in sparse arrays, so [,,] becomes [undefined, undefined].

@Gustavo 2016-03-15 12:36:36

I didn't get this kind of trouble yet. It seams a 3 elements collection results in an array with 2 elements. As for empty become undefined, it's a bit of JavaScript limitations, I gess you were expecting null instead of undefined, right?

@Gareth Davis 2009-12-09 08:14:13

For a cross browser implementation I'd sugguest you look at prototype.js $A function

copyed from 1.6.1:

function $A(iterable) {
  if (!iterable) return [];
  if ('toArray' in Object(iterable)) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

It doesn't use Array.prototype.slice probably because it isn't available on every browser. I'm afraid the performance is pretty bad as there a the fall back is a javascript loop over the iterable.

@Luc125 2011-11-13 13:12:58

The OP asked for an other way than "iterating through the contents of said collection and manually pushing each item into an array", but that's precisely what the $A function does most of the time.

@Gareth Davis 2011-11-13 19:45:44

I think the point I was trying to make is that there isn't a nice way to do it, the prototype.js code shows that you can look for a 'toArray' method but failing that iteration the safest route

@RobG 2016-01-04 09:49:07

This will create new, undefined members in sparse arrays. There should be a hasOwnProperty test before the assignment.

Related Questions

Sponsored Content

94 Answered Questions

[SOLVED] How do I remove a particular element from an array in JavaScript?

  • 2011-04-23 22:17:18
  • Walker
  • 6270306 View
  • 7831 Score
  • 94 Answer
  • Tags:   javascript arrays

68 Answered Questions

[SOLVED] What is the most efficient way to deep clone an object in JavaScript?

39 Answered Questions

[SOLVED] For-each over an array in JavaScript?

50 Answered Questions

59 Answered Questions

[SOLVED] How to compare arrays in JavaScript?

43 Answered Questions

[SOLVED] Loop through an array in JavaScript

34 Answered Questions

[SOLVED] Create ArrayList from array

15 Answered Questions

[SOLVED] Copy array items into another array

  • 2010-11-11 15:33:18
  • bba
  • 976095 View
  • 871 Score
  • 15 Answer
  • Tags:   javascript arrays

30 Answered Questions

[SOLVED] How to append something to an array?

15 Answered Questions

Sponsored Content