Le moyen le plus efficace pour convertir une HTMLCollection en tableau

392

Existe-t-il un moyen plus efficace de convertir une HTMLCollection en tableau, autre que d'itérer à travers le contenu de ladite collection et de pousser manuellement chaque élément dans un tableau?

À M
la source
10
Qu'entend-on par «efficace»? Si elle est la plus performante, une boucle for est généralement plus rapide que Array.prototype.slice . Une boucle travaille également dans une variété de navigateurs plus large (tous les ), donc par ces critères , il est le « moyen le plus efficace ». Et c'est très peu de code: for (var a=[], i=collection.length; i;) a[--i] = collection[i];donc pas beaucoup de "con" là :-)
RobG
@RobG Merci - je vous donnerais + 59k si je pouvais! ;-)
Slashback
1
En regardant les performances actuelles du navigateur , Slice a principalement rattrapé les boucles en termes de performances, sauf dans Chrome. En utilisant un plus grand nombre d'éléments et une légère optimisation de la boucle, les résultats sont presque identiques , sauf dans Chrome où une boucle est beaucoup plus rapide.
RobG
J'ai créé un test jsperf qui examine les deux méthodes mentionnées par @harpo ainsi qu'un test jquery pour les performances. J'ai trouvé que jquery est légèrement plus lent que les deux méthodes javascript et les meilleures performances varient entre les cas de test js. Chrome 59.0.3071 / Mac OS X 10.12.5 préfère utiliser Array.prototype.slice.callet Brave (basé sur Chrome 59.0.3071) n'a pratiquement aucune différence entre les deux tests javascript sur plusieurs exécutions. Voir jsperf.com/htmlcollection-array-vs-jquery-children
NuclearPeon
jsben.ch/h2IFA => test de performance pour les moyens les plus courants de le faire
EscapeNetscape

Réponses:

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

aura le même effet en utilisant du code "natif".

Éditer

Comme cela obtient beaucoup de vues, notez (selon le commentaire de @ oriol) que l'expression plus concise suivante est effectivement équivalente:

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

Mais notez selon le commentaire de @ JussiR, que contrairement au formulaire "verbeux", il crée une instance de tableau vide, inutilisée et en fait inutilisable dans le processus. Ce que les compilateurs font à ce sujet est en dehors du ken du programmeur.

Éditer

Depuis ECMAScript 2015 (ES 6) il y a aussi Array.from :

var arr = Array.from(htmlCollection);

Éditer

ECMAScript 2015 fournit également l' opérateur d'étalement , qui est fonctionnellement équivalent à Array.from(bien que la note prenne en Array.fromcharge une fonction de mappage comme deuxième argument).

var arr = [...htmlCollection];

J'ai confirmé que les deux ci-dessus fonctionnent NodeList.

Une comparaison des performances pour les méthodes mentionnées: http://jsben.ch/h2IFA

harpo
la source
7
Cela échoue dans IE6.
Heath Borders
29
Le raccourci [].slice.call(htmlCollection)fonctionne également.
Oriol
1
@ChrisNielsen Oui, j'ai été mal informé à ce sujet. Désolé d'avoir diffusé ça. Je ne savais pas que je l'avais également dit ici. Supprimé le commentaire pour éviter toute confusion mais pour le contexte, j'avais lu (ou mal lu) quelque part que le découpage d'une HTMLCollection le faisait se comporter comme un tableau et une collection. Totalement incorrect.
Erik Reppen
3
Le raccourci [] .slice n'est pas équivalent car il crée également une instance de tableau vide inutilisée. Je ne sais pas si les compilateurs sont capables de l'optimiser, cependant.
JussiR
3
Array.from, c'est-à-dire qu'il fromn'est pas pris en charge par IE11.
Frank Conijn
86

Je ne sais pas si c'est le plus efficace, mais une syntaxe ES6 concise pourrait être:

let arry = [...htmlCollection] 

Edit: Un autre, du commentaire de Chris_F:

let arry = Array.from(htmlCollection)
mido
la source
9
De plus, ES6 ajouteArray.from()
Chris_F
4
Attention au premier, il y a un bug subtil lors du transpiling avec babel où [... htmlCollection] retournera un tableau avec le htmlCollection car c'est son seul élément.
Marcel M.
3
L'opérateur de propagation de tableau ne fonctionne pas sur htmlCollection. Il ne s'applique qu'à NodeList.
Bobby
1
Array.from, c'est-à-dire qu'il fromn'est pas pris en charge par IE11.
Frank Conijn
Benchmark On dirait que l'opérateur de spread est plus rapide hors de ces 2.
RedSparr0w
20

J'ai vu une méthode plus concise pour obtenir des Array.prototypeméthodes en général qui fonctionne aussi bien. La conversion d'un HTMLCollectionobjet en Arrayobjet est illustrée ci-dessous:

[] .slice.call (votreHTMLCollectionObject);

Et, comme mentionné dans les commentaires, pour les anciens navigateurs tels que IE7 et les versions antérieures, vous devez simplement utiliser une fonction de compatibilité, comme:

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

    return a
}

Je sais que c'est une vieille question, mais je sentais que la réponse acceptée était un peu incomplète; donc je pensais que je jetterais ça là FWIW.

Fabricant de codes
la source
6

Pour une implémentation multi-navigateur, je vous suggère de regarder la fonction prototype.js $A

copié depuis 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;
}

Il n'utilise Array.prototype.sliceprobablement pas car il n'est pas disponible sur tous les navigateurs. Je crains que les performances ne soient assez mauvaises car il y a un repli est une boucle javascript sur le iterable.

Gareth Davis
la source
2
L'OP a demandé une autre solution que «parcourir le contenu de ladite collection et pousser manuellement chaque élément dans un tableau», mais c'est précisément ce que fait la $Afonction la plupart du temps.
Luc125
1
Je pense que le point que j'essayais de faire est qu'il n'y a pas une bonne façon de le faire, le code prototype.js montre que vous pouvez rechercher une méthode 'toArray' mais à défaut cette itération la route la plus sûre
Gareth Davis
1
Cela créera de nouveaux membres non définis dans des tableaux clairsemés. Il doit y avoir un test hasOwnProperty avant l'affectation.
RobG
3

Ceci est ma solution personnelle, basée sur les informations ici (ce fil):

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

Où $ A a été décrit par Gareth Davis dans son article:

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;
}

Si le navigateur prend en charge la meilleure façon, ok, sinon, vous utiliserez le navigateur croisé.

Gustavo
la source
En général, je ne m'attends pas à ce que try / catch soit un moyen efficace de gérer le flux de contrôle. Vous pouvez vérifier si la fonction existe en premier, puis exécuter l'un ou l'autre un peu moins cher.
Patrick
2
Comme avec la réponse de Gareth Davis, cela crée de nouveaux membres non définis dans des tableaux clairsemés, [,,]devient ainsi [undefined, undefined].
RobG
Je n'ai pas encore eu ce genre de problème. Il joint une collection de 3 éléments dans un tableau avec 2 éléments. Quant au vide devenu indéfini, c'est un peu de limitations JavaScript, je suppose que vous vous attendiez à null au lieu d'indéfini, non?
Gustavo
3

Cela fonctionne dans tous les navigateurs, y compris les versions antérieures d'IE.

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

Étant donné que jsperf est toujours en panne pour le moment, voici un jsfiddle qui compare les performances de différentes méthodes. https://jsfiddle.net/qw9qf48j/

Nicolas
la source
essayervar args = (htmlCollection.length === 1 ? [htmlCollection[0]] : Array.apply(null, htmlCollection));
Shahar Shokrani
3

Pour convertir un tableau en tableau de manière efficace, nous pouvons utiliser jQuery makeArray :

makeArray: convertit un objet de type tableau en un véritable tableau JavaScript.

Usage:

var domArray = jQuery.makeArray(htmlCollection);

Un petit plus:

Si vous ne souhaitez pas conserver de référence à l'objet tableau (la plupart du temps, HTMLCollections est modifié dynamiquement, il est donc préférable de les copier dans un autre tableau, cet exemple porte une attention particulière aux performances:

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.
}

Qu'est-ce qu'un tableau?

HTMLCollection est un "array-like"objet, semblable à un tableau d' objets sont similaires à l'objet de tableau mais il manque beaucoup de sa définition fonctionnelle:

Les objets de type tableau ressemblent à des tableaux. Ils ont divers éléments numérotés et une propriété de longueur. Mais c'est là que la similitude s'arrête. Les objets de type tableau n'ont aucune fonction de tableau et les boucles for-in ne fonctionnent même pas!

Shahar Shokrani
la source