1391 votes

Comment étendre un tableau JavaScript existant avec un autre tableau, sans créer un nouveau tableau ?

Il ne semble pas y avoir de moyen d'étendre un tableau JavaScript existant avec un autre tableau, c'est-à-dire d'émuler la méthode Python extend méthode.

Je souhaite atteindre les objectifs suivants :

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

Je sais qu'il y a un a.concat(b) mais elle crée un nouveau tableau au lieu de simplement étendre le premier. J'aimerais un algorithme qui fonctionne efficacement quand a est significativement plus grande que b (c'est-à-dire un qui ne copie pas a ).

<em><strong>Note : </strong>C'est <strong>pas un doublon de <a href="https://stackoverflow.com/questions/351409/appending-to-array">Comment ajouter quelque chose à un tableau ? </a></strong>-- le but ici est d'ajouter le contenu entier d'un tableau à l'autre, et de le faire "en place", c'est-à-dire sans copier tous les éléments du tableau étendu.</em>

9 votes

D'après le commentaire de @Toothbrush sur une réponse : a.push(...b) . Son concept est similaire à celui de la réponse précédente, mais il a été mis à jour pour ES6.

0 votes

>>> a.push(...b)

2061voto

DzinX Points 13452

Le site .push peut prendre plusieurs arguments. Vous pouvez utiliser la méthode opérateur de diffusion pour passer tous les éléments du deuxième tableau comme arguments à .push :

>>> a.push(...b)

Si votre navigateur ne prend pas en charge ECMAScript 6, vous pouvez utiliser .apply à la place :

>>> a.push.apply(a, b)

Ou peut-être, si vous pensez que c'est plus clair :

>>> Array.prototype.push.apply(a,b)

Veuillez noter que toutes ces solutions échoueront avec une erreur de débordement de pile si le tableau b est trop long (les problèmes commencent à partir d'environ 100 000 éléments, selon le navigateur).
Si vous ne pouvez pas garantir que b est suffisamment courte, vous devez utiliser une technique standard basée sur les boucles, décrite dans l'autre réponse.

3 votes

Je pense que c'est votre meilleure chance. Toute autre solution impliquera une itération ou une autre utilisation de apply().

69 votes

Cette réponse échouera si "b" (le tableau à étendre par) est grand (> 150000 entrées environ dans Chrome selon mes tests). Vous devriez utiliser une boucle for, ou encore mieux utiliser la fonction intégrée "forEach" sur "b". Voir ma réponse : stackoverflow.com/questions/1374126/

42 votes

@Deqing : Array's push peut prendre un nombre quelconque d'arguments, qui sont alors poussés à l'arrière du tableau. Ainsi, a.push('x', 'y', 'z') est un appel valide qui va étendre a par 3 éléments. apply est une méthode de toute fonction qui prend un tableau et utilise ses éléments comme s'ils étaient tous donnés explicitement comme éléments positionnels à la fonction. Ainsi, a.push.apply(a, ['x', 'y', 'z']) étendrait également le tableau de 3 éléments. En outre, apply prend un contexte comme premier argument (nous passons a encore là pour ajouter à a ).

235voto

jcdude Points 711

Vous devez utiliser une technique basée sur les boucles. D'autres réponses sur cette page qui sont basées sur l'utilisation de .apply peut échouer pour les grandes matrices.

Une implémentation assez laconique basée sur les boucles est :

Array.prototype.extend = function (other_array) {
    /* You should include a test to check whether other_array really is an array */
    other_array.forEach(function(v) {this.push(v)}, this);
}

Vous pouvez alors procéder comme suit :

var a = [1,2,3];
var b = [5,4,3];
a.extend(b);

La réponse de DzinX (en utilisant push.apply) et d'autres .apply échouent lorsque le tableau que nous ajoutons est de grande taille (les tests montrent que pour moi, la taille est > 150 000 entrées environ dans Chrome, et > 500 000 entrées dans Firefox). Vous pouvez voir cette erreur se produire dans ce jsperf .

Une erreur se produit car la taille de la pile d'appels est dépassée lorsque 'Function.prototype.apply' est appelé avec un grand tableau comme deuxième argument. (Le MDN contient une note sur les dangers du dépassement de la taille de la pile d'appel lors de l'utilisation de l'option Function.prototype.apply - voir la section intitulée "appliquer et fonctions intégrées").

Pour une comparaison de vitesse avec d'autres réponses sur cette page, voir ce jsperf (merci à EaterOfCode). L'implémentation basée sur les boucles est similaire en vitesse à l'utilisation de Array.push.apply mais a tendance à être un peu plus lent que l'option Array.slice.apply .

Il est intéressant de noter que si le tableau que vous ajoutez est clairsemé, la fonction forEach ci-dessus peut tirer parti de la sparsité et surpasser la méthode de la .apply des méthodes basées sur l'expérience ; consultez ce jsperf si vous voulez tester cela par vous-même.

À propos, ne soyez pas tenté (comme je l'ai été !) de raccourcir encore l'implémentation de forEach en :

Array.prototype.extend = function (array) {
    array.forEach(this.push, this);
}

car cela donne des résultats dérisoires ! Pourquoi ? Parce que Array.prototype.forEach fournit trois arguments à la fonction qu'il appelle - ceux-ci sont : (element_value, element_index, source_array). Tous ces arguments seront poussés dans votre premier tableau à chaque itération de la fonction forEach si vous utilisez "forEach(this.push, this)" !

1 votes

P.s. pour tester si other_array est vraiment un tableau, choisissez l'une des options décrites ici : stackoverflow.com/questions/767486/

7 votes

Bonne réponse. Je n'étais pas au courant de ce problème. J'ai cité votre réponse ici : stackoverflow.com/a/4156156/96100

2 votes

.push.apply est en fait beaucoup plus rapide que .forEach dans la v8, le plus rapide est toujours une boucle en ligne.

38voto

Jules Colle Points 2482

Si vous voulez utiliser jQuery, il y a $.merge()

Exemple :

a = [1, 2];
b = [3, 4, 5];
$.merge(a,b);

Résultat : a = [1, 2, 3, 4, 5]

1 votes

Comment puis-je trouver l'implémentation (dans le code source) de jQuery.merge() ?

1 votes

Cela m'a pris 10 minutes -- jQuery est open source et la source est publiée sur Github. J'ai fait une recherche pour "merge" et plus loin j'ai trouvé ce qui semblait être la définition de la fonction merge dans le fichier core.js, voir : github.com/jquery/jquery/blob/master/src/core.js#L331

18voto

J'aime le a.push.apply(a, b) décrite ci-dessus, et si vous le souhaitez, vous pouvez toujours créer une fonction de bibliothèque comme celle-ci :

Array.prototype.append = function(array)
{
    this.push.apply(this, array)
}

et l'utiliser comme ceci

a = [1,2]
b = [3,4]

a.append(b)

7 votes

La méthode push.apply ne doit pas être utilisée car elle peut provoquer un débordement de pile (et donc échouer) si votre argument "array" est un grand tableau (par exemple, > ~150000 entrées dans Chrome). Vous devez utiliser "array.forEach" - voir ma réponse : stackoverflow.com/a/17368101/1280629

0 votes

En ES6, il suffit d'utiliser a.push(...b)

14voto

Rafał Dowgird Points 16600

Il est possible de le faire en utilisant splice() :

b.unshift(b.length)
b.unshift(a.length)
Array.prototype.splice.apply(a,b) 
b.shift() // Restore b
b.shift() // 

Mais bien qu'il soit plus laid, il n'est pas plus rapide que push.apply en tout cas pas dans Firefox 3.0.

9 votes

J'ai constaté la même chose, splice n'apporte pas d'amélioration des performances en poussant chaque élément jusqu'à environ 10 000 tableaux d'éléments. jsperf.com/splice-vs-push

1 votes

+1 Merci d'avoir ajouté ce point et la comparaison des performances, cela m'a évité de tester cette méthode.

3 votes

L'utilisation de splice.apply ou de push.apply peut échouer en raison d'un débordement de pile si le tableau b est grand. Ils sont également plus lents que l'utilisation d'une boucle "for" ou "forEach" - voir ce jsPerf : jsperf.com/array-extending-push-vs-concat/5 et ma réponse stackoverflow.com/questions/1374126/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X