So erweitern Sie ein vorhandenes JavaScript-Array um ein anderes Array, ohne ein neues Array zu erstellen

971

Es scheint keine Möglichkeit zu geben, ein vorhandenes JavaScript-Array um ein anderes Array zu erweitern, dh die Python- extendMethode zu emulieren .

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ß, dass es eine a.concat(b)Methode gibt, aber sie erstellt ein neues Array, anstatt einfach das erste zu erweitern. Ich möchte einen Algorithmus, der effizient funktioniert, wenn er awesentlich größer ist als b(dh einer, der nicht kopiert a).

Hinweis: Dies ist kein Duplikat von Wie man etwas an ein Array anfügt? - Das Ziel hier ist es, den gesamten Inhalt eines Arrays zum anderen hinzuzufügen und dies "an Ort und Stelle" zu tun, dh ohne alle Elemente des erweiterten Arrays zu kopieren.

DzinX
quelle
5
Aus dem Kommentar von @ Toothbrush zu einer Antwort : a.push(...b). Das Konzept ähnelt der Top-Antwort, wurde jedoch für ES6 aktualisiert.
Tscizzle

Antworten:

1498

Die .pushMethode kann mehrere Argumente annehmen. Mit dem Spread-Operator können Sie alle Elemente des zweiten Arrays als Argumente an Folgendes übergeben .push:

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

Wenn Ihr Browser ECMAScript 6 nicht unterstützt, können Sie .applystattdessen Folgendes verwenden :

>>> 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 das Array bzu lang ist (Probleme beginnen je nach Browser bei etwa 100.000 Elementen).
Wenn Sie nicht garantieren können, dass dies bkurz genug ist, sollten Sie eine in der anderen Antwort beschriebene standardmäßige schleifenbasierte Technik verwenden.

DzinX
quelle
3
Ich denke, das ist die beste Wahl. Alles andere wird eine Iteration oder eine andere Anstrengung von apply () beinhalten
Peter Bailey
44
Diese Antwort schlägt fehl, wenn "b" (das Array, um das erweitert werden soll) groß ist (nach meinen Tests ca. 150000 Einträge in Chrome). Sie sollten eine for-Schleife verwenden oder noch besser die eingebaute Funktion "forEach" für "b" verwenden. Siehe meine Antwort: stackoverflow.com/questions/1374126/…
jcdude
35
@Deqing: Die Array- pushMethode kann eine beliebige Anzahl von Argumenten annehmen, die dann an die Rückseite des Arrays verschoben werden . Dies a.push('x', 'y', 'z')gilt auch für einen gültigen Aufruf, der um a3 Elemente erweitert wird. applyist eine Methode für jede Funktion, die ein Array verwendet und seine Elemente so verwendet, als ob sie alle explizit als Positionselemente für die Funktion angegeben würden. So a.push.apply(a, ['x', 'y', 'z'])erstrecken würde auch die Anordnung von 3 Elementen. Nimmt zusätzlich applyeinen Kontext als erstes Argument (wir übergeben ihn dort aerneut, um ihn anzuhängen a).
DzinX
Hinweis: In diesem Szenario ist der erste Rückgabewert nicht [1,2,3,4,5], sondern 5, während a == [1,2,3,4,5] danach.
Jawo
1
Ein weiterer etwas weniger verwirrender (und nicht viel längerer) Aufruf wäre : [].push.apply(a, b).
niry
259

Update 2018 : Eine bessere Antwort ist eine neuere von mir : a.push(...b). Stimmen Sie diesem nicht mehr zu, da es die Frage nie wirklich beantwortet hat, aber es war ein Hack von 2015 um den ersten Treffer bei Google :)


Für diejenigen, die einfach nach "JavaScript Array Extend" gesucht haben und hierher gekommen sind, können Sie sehr gut verwenden Array.concat.

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

Concat gibt eine Kopie des neuen Arrays zurück, da der Thread-Starter dies nicht wollte. Aber es könnte Sie nicht interessieren (sicherlich für die meisten Arten von Anwendungen wird dies in Ordnung sein).


Es gibt auch einige nette ECMAScript 6-Zucker in Form des Spread-Operators:

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

(Es kopiert auch.)

Odinho - Velmont
quelle
43
Die Frage lautete eindeutig: "Ohne ein neues Array zu erstellen?"
Wilt
10
@Wilt: Das ist wahr, die Antwort sagt warum aber :) Dies wurde zum ersten Mal seit langer Zeit bei Google getroffen. Auch die anderen Lösungen waren hässlich, wollten etwas Ideomatisches und Nettes. Obwohl heutzutage die arr.push(...arr2)neuere und bessere und eine technisch korrekte (tm) Antwort auf diese spezielle Frage ist.
Odinho - Velmont
7
Ich höre dich, aber ich habe nach "Javascript-Array anhängen, ohne ein neues Array zu erstellen" gesucht. Dann ist es traurig zu sehen, dass eine 60-mal hochgestimmte Antwort, die hoch oben ist, die eigentliche Frage nicht beantwortet;) Ich habe nicht downvotiert, da hast du es in deiner antwort klar gemacht.
Wilt
202

Sie sollten eine schleifenbasierte Technik verwenden. Andere Antworten auf dieser Seite, die auf der Verwendung basieren, .applykönnen für große Arrays fehlschlagen.

Eine ziemlich 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 Folgendes tun:

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

Die Antwort von DzinX (mit push.apply) und andere .applybasierte Methoden schlagen fehl, wenn das Array, das wir anhängen, groß ist (Tests zeigen, dass für mich ungefähr 150.000 Einträge in Chrome und> 500.000 Einträge in Firefox groß sind). Sie können diesen Fehler in diesem jsperf sehen .

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 zu den Gefahren einer Überschreitung der Aufrufstapelgröße mithilfe von Function.prototype.apply - siehe Abschnitt "Anwenden und integrierte Funktionen".)

Einen Geschwindigkeitsvergleich mit anderen Antworten auf dieser Seite finden Sie in diesem jsperf (dank EaterOfCode). Die schleifenbasierte Implementierung ähnelt in ihrer Geschwindigkeit der Verwendung Array.push.apply, ist jedoch tendenziell etwas langsamer als Array.slice.apply.

Interessanterweise forEachkann die oben beschriebene Methode die Sparsamkeit ausnutzen und die .applybasierten Methoden übertreffen , wenn das Array, das Sie anhängen, spärlich ist . Schauen Sie sich dieses jsperf an, wenn Sie dies selbst testen möchten.

Übrigens, seien Sie nicht versucht (wie ich!), Die forEach-Implementierung weiter zu verkürzen auf:

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

weil dies zu Müll führt! Warum? Da Array.prototype.forEachdie aufgerufene Funktion drei Argumente enthält: (element_value, element_index, source_array). All dies wird bei jeder Iteration auf Ihr erstes Array übertragen, forEachwenn Sie "forEach (this.push, this)" verwenden!

jcdude
quelle
1
ps um zu testen, ob other_array wirklich ein Array ist, wählen Sie eine der hier beschriebenen Optionen: stackoverflow.com/questions/767486/…
jcdude
6
Gute Antwort. Ich war mir dieses Problems nicht bewusst. Ich habe Ihre Antwort hier zitiert: stackoverflow.com/a/4156156/96100
Tim Down
2
.push.applyist eigentlich viel schneller als .forEachin v8, die schnellste ist immer noch eine Inline-Schleife.
Benjamin Gruenbaum
1
@BenjaminGruenbaum - könntest du einen Link zu einigen Ergebnissen posten, die das zeigen? Soweit ich aus den am Ende dieses Kommentars verlinkten jsperf-Ergebnissen ersehen kann, ist die Verwendung .forEachschneller als .push.applyin Chrome / Chromium (in allen Versionen seit Version 25). Ich konnte v8 nicht isoliert testen, aber wenn ja, verknüpfen Sie bitte Ihre Ergebnisse. Siehe jsperf: jsperf.com/array-extending-push-vs-concat/5
jcdude
1
@jcdude dein jsperf ist ungültig. wie alle Elemente im Array sind undefined, so .forEachwerden sie überspringen, es ist die schnellste machen. .spliceist eigentlich der schnellste?! jsperf.com/array-extending-push-vs-concat/18
EaterOfCode
152

Ich fühle mich heutzutage am elegantesten:

arr1.push(...arr2);

Der MDN-Artikel über den Spread-Operator erwähnt diesen schönen zuckerhaltigen Weg in ES2015 (ES6):

Ein besserer Schub

Beispiel: Push wird häufig verwendet, um ein Array an das Ende eines vorhandenen Arrays zu verschieben. In ES5 wird dies häufig wie folgt durchgeführt:

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 dies:

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

Beachten Sie, dass arr2dies nicht sehr groß sein kann (halten Sie es unter etwa 100 000 Elementen), da der Aufrufstapel gemäß der Antwort von jcdude überläuft.

Odinho - Velmont
quelle
1
Nur eine Warnung vor einem Fehler, den ich gerade gemacht habe. Die ES6-Version ist sehr nah dran arr1.push(arr2). Dies kann ein Problem sein, so dass arr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2)das Ergebnis ein Array von Arrays ist arr1 == [['a','b','d']]und nicht zwei Arrays zusammen. Es ist leicht, einen Fehler zu machen. Aus diesem Grund bevorzuge ich Ihre zweite Antwort unten. stackoverflow.com/a/31521404/4808079
Seph Reed
Bei der Verwendung .concatist es praktisch, dass das zweite Argument kein Array sein darf. Dies kann mit durch die Kombination von Spread - Operator erreicht werden concat, zBarr1.push(...[].concat(arrayOrSingleItem))
Steven Pribilinskiy
Dies ist modernes, elegantes Javascript für Fälle, in denen das Zielarray nicht leer ist.
Timbo
Ist das schnell genug?
Roel
@RoelDelosReyes: Ja.
Odinho - Velmont
46

Zuerst ein paar Worte zu apply()JavaScript, um zu verstehen, warum wir es verwenden:

Die apply()Methode ruft eine Funktion mit einem bestimmten thisWert und Argumenten auf, die als Array bereitgestellt werden.

Push erwartet eine Liste von Elementen, die dem Array hinzugefügt werden sollen. Die apply()Methode verwendet jedoch die erwarteten Argumente für den Funktionsaufruf als Array. Dies ermöglicht es uns, pushdie Elemente eines Arrays mit der eingebauten push()Methode einfach in ein anderes Array zu verschieben .

Stellen Sie sich vor, Sie haben diese Arrays:

var a = [1, 2, 3, 4];
var b = [5, 6, 7];

und mach das einfach:

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

Das Ergebnis wird sein:

a = [1, 2, 3, 4, 5, 6, 7];

Dasselbe kann in ES6 mit dem Spread-Operator (" ...") wie folgt gemacht werden:

a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7]; 

Kürzer und besser, aber derzeit nicht in allen Browsern vollständig unterstützt.

Auch wenn Sie wollen bewegen alles von Array bzua , Entleerung bin den Prozess, können Sie dies tun:

while(b.length) {
  a.push(b.shift());
} 

und das Ergebnis wird wie folgt sein:

a = [1, 2, 3, 4, 5, 6, 7];
b = [];
Alireza
quelle
1
oder while (b.length) { a.push(b.shift()); }, richtig?
LarsW
37

Wenn Sie jQuery verwenden möchten, gibt es $ .merge ()

Beispiel:

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

Ergebnis: a = [1, 2, 3, 4, 5]

Jules Colle
quelle
1
Wie finde ich die Implementierung (im Quellcode) von jQuery.merge()?
ma11hew28
Ich habe 10 Minuten gebraucht - jQuery ist Open Source und die Quelle wird auf Github veröffentlicht. Ich habe nach "Zusammenführen" gesucht und weiter die Definition der Zusammenführungsfunktion in der Datei core.js gefunden, siehe: github.com/jquery/jquery/blob/master/src/core.js#L331
Amn
19

Ich mag die a.push.apply(a, b)oben beschriebene Methode, und wenn Sie möchten, können Sie immer eine Bibliotheksfunktion wie folgt erstellen:

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

und benutze es so

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

a.append(b)
Pizzaiola Gorgonzola
quelle
6
Die push.apply-Methode sollte nicht verwendet werden, da sie einen Stapelüberlauf verursachen kann (und daher fehlschlägt), wenn Ihr "Array" -Argument ein großes Array ist (z. B.> ~ 150000 Einträge in Chrome). Sie sollten "array.forEach" verwenden - siehe meine Antwort: stackoverflow.com/a/17368101/1280629
jcdude
12

Es ist möglich, dies zu tun mit splice():

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

Aber obwohl es hässlicher ist, ist es nicht schneller als push.apply, zumindest nicht in Firefox 3.0.

Rafał Dowgird
quelle
9
Ich habe das Gleiche festgestellt: Spleiß bietet keine Leistungsverbesserungen für das Verschieben
Drew
1
+1 Vielen Dank, dass Sie dies und den Leistungsvergleich hinzugefügt haben. Das hat mir die Mühe erspart, diese Methode zu testen.
jjrv
1
Die Verwendung von splice.apply oder push.apply kann aufgrund eines Stapelüberlaufs fehlschlagen, wenn Array b groß ist. Sie sind auch langsamer als die Verwendung einer "for" - oder "forEach" -Schleife - siehe jsPerf: jsperf.com/array-extending-push-vs-concat/5 und meine Antwort stackoverflow.com/questions/1374126/…
jcdude
4

Diese Lösung funktioniert für mich (mit dem Spread-Operator von ECMAScript 6):

let array = ['my', 'solution', 'works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); // Adding to same array
newArray2.push([...array]); // Adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);

Pankaj Shrivastava
quelle
1

Sie können eine Polyfüllung zum Erweitern erstellen, wie unten beschrieben. Es wird dem Array hinzugefügt. an Ort und Stelle und geben Sie sich zurück, so dass Sie andere Methoden verketten können.

if (Array.prototype.extend === undefined) {
  Array.prototype.extend = function(other) {
    this.push.apply(this, arguments.length > 1 ? arguments : other);
    return this;
  };
}

function print() {
  document.body.innerHTML += [].map.call(arguments, function(item) {
    return typeof item === 'object' ? JSON.stringify(item) : item;
  }).join(' ') + '\n';
}
document.body.innerHTML = '';

var a = [1, 2, 3];
var b = [4, 5, 6];

print('Concat');
print('(1)', a.concat(b));
print('(2)', a.concat(b));
print('(3)', a.concat(4, 5, 6));

print('\nExtend');
print('(1)', a.extend(b));
print('(2)', a.extend(b));
print('(3)', a.extend(4, 5, 6));
body {
  font-family: monospace;
  white-space: pre;
}

Mr. Polywhirl
quelle
0

Die Antworten kombinieren ...

Array.prototype.extend = function(array) {
    if (array.length < 150000) {
        this.push.apply(this, array)
    } else {
        for (var i = 0, len = array.length; i < len; ++i) {
            this.push(array[i]);
        };
    }  
}
zCoder
quelle
9
Nein, das ist nicht nötig. Eine for-Schleife mit forEach ist schneller als die Verwendung von push.apply und funktioniert unabhängig von der Länge des Arrays, um das erweitert werden soll. Schauen Sie sich meine überarbeitete Antwort an: stackoverflow.com/a/17368101/1280629 Woher wissen Sie auf jeden Fall, dass 150000 die richtige Nummer für alle Browser ist? Das ist ein Fudge.
JCdude
lol. Meine Antwort ist nicht wiederzuerkennen, scheint aber eine Kombination aus anderen zu sein, die zu dieser Zeit gefunden wurden - dh eine Zusammenfassung. Keine Sorge
zCoder
0

Eine weitere Lösung zum Zusammenführen von mehr als zwei Arrays

var a = [1, 2],
    b = [3, 4, 5],
    c = [6, 7];

// Merge the contents of multiple arrays together into the first array
var mergeArrays = function() {
 var i, len = arguments.length;
 if (len > 1) {
  for (i = 1; i < len; i++) {
    arguments[0].push.apply(arguments[0], arguments[i]);
  }
 }
};

Rufen Sie dann an und drucken Sie als:

mergeArrays(a, b, c);
console.log(a)

Ausgabe wird sein: Array [1, 2, 3, 4, 5, 6, 7]

user3126309
quelle
-1

Die Antwort ist super einfach.

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
(The following code will combine the two arrays.)

a = a.concat(b);

>>> a
[1, 2, 3, 4, 5]

Concat verhält sich sehr ähnlich wie die Verkettung von JavaScript-Zeichenfolgen. Es wird eine Kombination der Parameter zurückgegeben, die Sie in die concat-Funktion am Ende des Arrays eingegeben haben, für das Sie die Funktion aufrufen. Der springende Punkt ist, dass Sie den zurückgegebenen Wert einer Variablen zuweisen müssen, sonst geht er verloren. So zum Beispiel

a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it doesn't do anything with it.
Timothy Trousdale
quelle
2
Wie in den Dokumenten für Array.prototype.concat()bei MDN klar angegeben, wird ein neues Array zurückgegeben , es wird nicht an das vorhandene Array angehängt, wie OP eindeutig gefragt hat.
Wilt
-2

Verwenden Sie Array.extendstatt Array.pushfür> 150.000 Datensätze.

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }

    for (let record of arr) {
      this.push(record);
    }

    return this;
  };
}
Mahdi Pedram
quelle
-6

Super einfach, zählt nicht auf Spread-Operatoren oder gilt, wenn das ein Problem ist.

b.map(x => a.push(x));

Nach einigen Leistungstests ist dies furchtbar langsam, beantwortet jedoch die Frage, ob kein neues Array erstellt werden soll. Concat ist deutlich schneller, selbst jQuery $.merge()macht es klar.

https://jsperf.com/merge-arrays19b/1

elPastor
quelle
4
Sie sollten den Rückgabewert verwenden, .forEachanstatt ihn zu verwenden .map. Es vermittelt besser die Absicht Ihres Codes.
David
@ David - für jeden sein eigenes. Ich bevorzuge die Synax von, .mapaber Sie könnten auch tun b.forEach(function(x) { a.push(x)} ). Tatsächlich habe ich das zum jsperf-Test hinzugefügt und es ist ein Haar schneller als die Karte. Immer noch super langsam.
elPastor
3
Dies ist kein Präferenzfall. Diese "funktionalen" Methoden haben jeweils eine bestimmte Bedeutung. Hier anzurufen .mapsieht sehr komisch aus. Es wäre wie ein Anruf b.filter(x => a.push(x)). Es funktioniert, aber es verwirrt den Leser, indem es impliziert, dass etwas passiert, wenn es nicht passiert.
David
2
@elPastor, es sollte Ihre Zeit wert sein, denn ein anderer Typ wie ich würde sitzen und sich am Nacken kratzen und sich fragen, was sonst noch in Ihrem Code passiert, den er nicht sehen kann. In den meisten Fällen kommt es auf die Klarheit der Absicht an, nicht auf die Leistung. Bitte respektieren Sie die Zeit derer, die Ihren Code lesen, denn es können viele sein, wenn Sie nur einer sind. Vielen Dank.
Dmytro Y.
1
@elPastor Ich weiß genau was .map()macht und das ist das Problem. Dieses Wissen würde mich denken lassen, dass Sie das resultierende Array benötigen, andernfalls wäre es eine gute Sache .forEach(), den Leser über die Nebenwirkungen der Rückruffunktion vorsichtig zu machen. Genau genommen erstellt Ihre Lösung ein zusätzliches Array natürlicher Zahlen (Ergebnisse von push), während die Frage lautet: "Ohne ein neues Array zu erstellen".
Dmytro Y.