1464 Stimmen

Wie man ein bestehendes JavaScript-Array mit einem anderen Array erweitert, ohne ein neues Array zu erstellen

Es scheint keine Möglichkeit zu geben, ein bestehendes JavaScript-Array mit einem anderen Array zu erweitern, d.h. Pythons extend Methode.

Ich möchte Folgendes erreichen:

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

Ich weiß, es gibt eine a.concat(b) Methode, aber sie erstellt ein neues Array, anstatt einfach das erste zu erweitern. Ich hätte gerne einen Algorithmus, der effizient arbeitet, wenn a deutlich größer ist als b (d.h. eine, die nicht kopiert a ).

<em><strong>Nota: </strong>Dies ist <strong>nicht ein Duplikat von <a href="https://stackoverflow.com/questions/351409/appending-to-array">Wie kann man etwas an ein Array anhängen? </a></strong>-- Ziel ist es hier, den gesamten Inhalt eines Arrays dem anderen hinzuzufügen, und zwar "an Ort und Stelle", d.h. ohne alle Elemente des erweiterten Arrays zu kopieren.</em>

10 Stimmen

Aus @Zahnbürste's Kommentar zu einer Antwort: a.push(...b) . Das Konzept ähnelt dem der ersten Antwort, wurde aber für ES6 aktualisiert.

0 Stimmen

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

2141voto

DzinX Punkte 51124

Le site .push Methode kann mehrere Argumente annehmen. Sie können die Spread-Operator um alle Elemente des zweiten Arrays als Argumente an .push :

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

Wenn Ihr Browser ECMAScript 6 nicht unterstützt, können Sie .apply stattdessen:

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

Oder vielleicht, wenn Sie denken, dass es klarer ist:

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

Bitte beachten Sie, dass alle diese Lösungen mit einem Stapelüberlauffehler fehlschlagen, wenn array b zu lang ist (je nach Browser beginnen die Probleme bei etwa 100.000 Elementen).
Wenn Sie nicht garantieren können, dass b kurz genug ist, sollten Sie eine Standardschleifentechnik verwenden, die in der anderen Antwort beschrieben wurde.

357voto

odinho - Velmont Punkte 19511

<strong>Update 2018 </strong>: <a href="https://stackoverflow.com/questions/1374126/how-to-extend-an-existing-javascript-array-with-another-array-without-creating#35223022">Eine bessere Antwort ist eine neuere von mir </a>: <code>a.push(...b)</code> . Bewerten Sie diesen Beitrag nicht mehr, da er die Frage nie wirklich beantwortet hat, aber es war ein Hack aus dem Jahr 2015, um den ersten Treffer bei Google zu landen :)


Für diejenigen, die einfach nach "JavaScript array extend" gesucht haben und hier gelandet sind, können Sie sehr gut verwenden Array.concat .

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

Concat wird eine Kopie des neuen Arrays zurückgeben, was der Thread-Starter nicht wollte. Aber vielleicht ist Ihnen das egal (für die meisten Verwendungszwecke ist das sicher in Ordnung).


Es gibt auch einige nette ECMAScript 6 Zucker für diese in Form von der Spread-Operator:

const a = [1, 2, 3];
const b = [...a, 5, 4, 3];

(Es wird auch kopiert.)

238voto

jcdude Punkte 2745

Sie sollten eine schleifenbasierte Technik verwenden. Andere Antworten auf dieser Seite, die auf der Verwendung von .apply kann bei großen Arrays fehlschlagen.

Eine recht knappe schleifenbasierte Implementierung ist:

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

Sie können dann wie folgt vorgehen:

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

DzinX's Antwort (mit push.apply) und andere .apply basierten Methoden scheitern, wenn das Array, das wir anhängen, groß ist (Tests zeigen, dass für mich groß ist > 150.000 Einträge ca. in Chrome und > 500.000 Einträge in Firefox). Sie können diesen Fehler sehen, der in dieser jsperf .

Ein Fehler tritt auf, weil die Größe des Aufrufstapels überschritten wird, wenn 'Function.prototype.apply' mit einem großen Array als zweitem Argument aufgerufen wird. (MDN hat einen Hinweis auf die Gefahren des Überschreitens der Aufrufstapelgröße mit Function.prototype.apply - siehe Abschnitt "Anwenden und integrierte Funktionen").

Einen Geschwindigkeitsvergleich mit anderen Antworten auf dieser Seite finden Sie unter dieser jsperf (Dank an EaterOfCode). Die schleifenbasierte Implementierung ist von der Geschwindigkeit her ähnlich wie die Verwendung von Array.push.apply ist aber tendenziell etwas langsamer als Array.slice.apply .

Interessanterweise, wenn das Array, das Sie anfügen, spärlich ist, wird die forEach basierte Methode kann die Vorteile der Sparsamkeit nutzen und übertrifft die .apply basierten Methoden; siehe auch dieser jsperf wenn Sie dies selbst testen möchten.

Übrigens, lassen Sie sich nicht dazu verleiten (wie ich es getan habe!), die forEach-Implementierung weiter zu verkürzen:

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

denn das führt zu miserablen Ergebnissen! Und warum? Weil Array.prototype.forEach liefert drei Argumente für die aufgerufene Funktion - diese sind: (element_value, element_index, source_array). Alle diese Argumente werden bei jeder Iteration der Funktion in das erste Array eingefügt. forEach wenn Sie "forEach(this.push, this)" verwenden!

187voto

odinho - Velmont Punkte 19511

Ich finde, das ist heutzutage am elegantesten:

arr1.push(...arr2);

Le site MDN-Artikel über den Spread-Operator erwähnt diese nette zuckersüße Art in ES2015 (ES6):

Ein besserer Anstoß

Beispiel: push wird oft verwendet, um ein Array an das Ende eines bestehenden Arrays zu schieben. In ES5 wird dies oft so gemacht:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

In ES6 mit Spread wird daraus:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

Bitte beachten Sie, dass arr2 darf nicht zu groß sein (unter 100 000 Elementen), da der Aufrufstapel sonst überläuft, wie in der Antwort von jcdude beschrieben.

90voto

Luc Punkte 4101

Übersicht

  • a.push(...b) - begrenzte, schnelle, moderne Syntax
  • a.push.apply(a, b) - begrenzt, schnell
  • a = a.concat(b) unbegrenzt, langsam, wenn a groß ist
  • for (let i in b) { a.push(b[i]); } - unbegrenzt, langsam, wenn b groß ist

Jedes Snippet ändert die a zu erweitern mit b .

Die "begrenzten" Schnipsel übergeben jedes Array-Element als Argument, und die maximale Anzahl der Argumente, die Sie einer Funktion übergeben können, ist begrenzt . Aus diesem Link geht hervor, dass a.push(...b) ist zuverlässig, bis es etwa 32k Elemente in b (die Größe von a spielt keine Rolle).

Einschlägige MDN-Dokumentation: Verbreitungssyntax , .apply() , .concat() , .push()

Überlegungen zur Geschwindigkeit

Jede Methode ist schnell, wenn sowohl a y b sind klein, daher werden Sie in den meisten Webanwendungen die push(...b) und damit fertig werden.

Wenn Sie mit mehr als ein paar tausend Elementen arbeiten, hängt es von der jeweiligen Situation ab, was Sie tun wollen:

  • Sie fügen ein paar Elemente zu einem großen Array hinzu
    push(...b) ist sehr schnell
  • Sie fügen viele Elemente zu einem großen Array hinzu
    concat ist etwas schneller als eine Schleife
  • Sie fügen viele Elemente zu einem kleinen Array hinzu
    concat ist viel schneller als eine Schleife
  • Sie sind in der Regel Hinzufügen von nur wenigen Elementen zu einem Array beliebiger Größe
    Schleifen sind etwa so schnell wie die eingeschränkten Methoden für kleine Additionen, werfen aber nie eine Ausnahme, auch wenn sie nicht die leistungsfähigste ist, wenn Sie viele Elemente hinzufügen
  • Sie schreiben eine Wrapper-Funktion, um immer die maximale Leistung zu erhalten
    müssen Sie die Längen der Eingaben dynamisch überprüfen und die richtige Methode wählen, indem Sie vielleicht push(...b_part) (mit Scheiben des großen b ) in einer Schleife.

Das hat mich überrascht: Ich dachte a=a.concat(b) in der Lage wäre, ein schönes Memcpy von b auf a ohne sich die Mühe zu machen, einzelne Erweiterungsoperationen durchzuführen wie a.push(...b) zu tun hätte und somit immer der Schnellste wäre. Stattdessen, a.push(...b) ist viel, viel schneller, besonders wenn a ist groß.

Die Geschwindigkeit der verschiedenen Methoden wurde in Firefox 88 unter Linux gemessen:

a = [];
for (let i = 0; i < Asize; i++){
  a.push(i);
}
b = [];
for (let i = 0; i < Bsize; i++){
  b.push({something: i});
}
t=performance.now();
// Code to test
console.log(performance.now() - t);

Parameter und Ergebnisse:

 ms | Asize | Bsize | code
----+-------+-------+------------------------------
 ~0 |  any  |  any  | a.push(...b)
 ~0 |  any  |  any  | a.push.apply(a, b)
480 |   10M |    50 | a = a.concat(b)
  0 |   10M |    50 | for (let i in b) a.push(b[i])
506 |   10M |  500k | a = a.concat(b)
882 |   10M |  500k | for (let i in b) a.push(b[i])
 11 |    10 |  500k | a = a.concat(b)
851 |    10 |  500k | for (let i in b) a.push(b[i])

Beachten Sie, dass eine Bsize von 500 000 ist der größte Wert, der von allen Methoden auf meinem System akzeptiert wird, deshalb ist er kleiner als Asize .

Alle Tests wurden mehrfach durchgeführt, um festzustellen, ob es sich um Ausreißer oder repräsentative Ergebnisse handelt. Die schnellen Methoden sind in nur einem Durchgang fast unmessbar. performance.now() Aber da die langsamen Methoden so offensichtlich sind und die beiden schnellen Methoden beide auf demselben Prinzip beruhen, brauchen wir es nicht mehrmals zu wiederholen, um Haare zu spalten.

Le site concat Methode ist immer langsam, wenn eines der beiden Arrays groß ist, aber die Schleife ist nur langsam, wenn sie viele Funktionsaufrufe durchführen muss und es keine Rolle spielt, wie groß a ist. Eine Schleife ist also ähnlich wie push(...b) o push.apply für kleine b s, aber ohne zu brechen, wenn sie groß wird; wenn Sie sich jedoch der Grenze nähern, concat wieder ein bisschen schneller ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X