Kopieren Sie Array-Elemente in ein anderes Array

918

Ich habe ein JavaScript-Array, dataArraydas ich in ein neues Array verschieben möchte newArray. Außer ich will nicht newArray[0]sein dataArray. Ich möchte alle Elemente in das neue Array einfügen:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...

oder noch besser:

var newArray = new Array (
   dataArray1.values(),
   dataArray2.values(),
   // ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself
);

Das neue Array enthält nun alle Werte der einzelnen Datenarrays. Gibt es eine Kurzform wie " pushValuesverfügbar", damit ich nicht jedes einzelne durchlaufen dataArrayund die Elemente einzeln hinzufügen muss?

bba
quelle
Dies sollte die Antwort sein davidwalsh.name/combining-js-arrays
starikovs

Antworten:

1248

Verwenden Sie die Concat- Funktion wie folgt :

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);

Der Wert von newArraywird [1, 2, 3, 4]( arrayAund arrayBbleibt unverändert; concaterstellt und gibt ein neues Array für das Ergebnis zurück).

WiseGuyEh
quelle
16
Ich bin damit einverstanden, dass die performante Ausführung sehr nett ist. ABER ist nicht genau zu diesem Zweck concat zu concat auf Arrays zu konzentrieren? Es sollte also Standard sein . Oder gibt es andere bessere Dinge mit concat zu tun? Und es kann langsam sein, nur weil die JS-Engine des Browsers schlecht implementiert ist oder wo immer Sie sie verwenden? Es könnte eines Tages behoben werden. Ich würde die Wartbarkeit von Code gegenüber Optimierungen der Hacky-Geschwindigkeit wählen. Hmm ....
Bitterblue
3
Außerdem habe ich gerade die Situation verglichen: concat vs. push.apply. Google Chrome: schnell (concat = Gewinner) , Opera: schnell (concat = Gewinner) , IE: langsamer (concat = Gewinner) , Firefox: langsam (push.apply = Gewinner, aber 10-mal langsamer als Chrome's concat) ... sprechen von einer schlechten Implementierung der JS-Engine .
Bitterblau
15
Wie ist die Verkettung von zwei Arrays die akzeptierte Antwort, um sie ineinander zu verschieben?! Das sind zwei verschiedene Operationen.
Kaqqao
@kaqqao, weil pushein Array von Werten nicht abgeflacht wird. concaterreicht, was die Frage erfordert.
WiseGuyEh
644

Vorausgesetzt, Ihre Arrays sind nicht sehr groß (siehe Einschränkung unten), können Sie die push()Methode des Arrays verwenden, an das Sie Werte anhängen möchten. push()kann mehrere Parameter annehmen, sodass Sie die apply()Methode verwenden können, um das zu pushende Array von Werten als Liste von Funktionsparametern zu übergeben. Dies hat den Vorteil concat(), dass dem Array Elemente hinzugefügt werden, anstatt ein neues Array zu erstellen.

Es scheint jedoch, dass bei großen Arrays (in der Größenordnung von 100.000 Mitgliedern oder mehr) dieser Trick fehlschlagen kann . Für solche Arrays ist die Verwendung einer Schleife ein besserer Ansatz. Weitere Informationen finden Sie unter https://stackoverflow.com/a/17368101/96100 .

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);

Vielleicht möchten Sie dies in eine Funktion verallgemeinern:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}

... oder zum ArrayPrototyp hinzufügen :

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);

... oder das Original emulieren push() Verfahren durch mehrere Parameter unter Verwendung der Tatsache ermöglicht , dass concat(), wie push(), mehrere Parameter erlaubt:

Array.prototype.pushArray = function() {
    this.push.apply(this, this.concat.apply([], arguments));
};

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);

Hier ist eine schleifenbasierte Version des letzten Beispiels, die für große Arrays und alle gängigen Browser geeignet ist, einschließlich IE <= 8:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};
Tim Down
quelle
9
Hinweis: newArray.push.apply(newArray, dataArray1);gibt das gleiche wieArray.prototype.push.applay(newArray,dataArra1);
Ist dies mit älteren Browsern kompatibel?
Damien Ó Ceallaigh
1
@ DamienÓCeallaigh: Ja. Dies ist alles kompatibel mit Browsern, die auf IE 6 und noch früher zurückgehen.
Tim Down
Dies ist der Inbegriff für schlechte Programmierpraktiken. Array.prototype.concatist nicht schlecht, sondern wird einfach missverstanden und manchmal missbraucht. Unter diesen Umständen wird es nur missverstanden, aber nicht missbraucht.
Jack Giffin
446

Ich werde noch eine "zukunftssichere" Antwort hinzufügen

In ECMAScript 6 können Sie die Spread-Syntax verwenden :

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

console.log(arr1)

Die Spread-Syntax ist noch nicht in allen gängigen Browsern enthalten. Informationen zur aktuellen Kompatibilität finden Sie in dieser (ständig aktualisierten) Kompatibilitätstabelle .

Sie können jedoch die Spread-Syntax mit Babel.js verwenden .

bearbeiten:

Weitere Kommentare zur Leistung finden Sie in der Antwort von Jack Giffin unten. Es scheint, dass concat immer noch besser und schneller ist als der Spread-Operator.

Karel Bílek
quelle
7
Sie können auch den Spread-Operator verwenden, wenn Sie TypeScript verwenden. Wenn Sie auf ES5 abzielen, wird es kompiliert newArray.apply(newArray, dataArray1).
AJ Richardson
5
Hinweis: Wenn Sie das Ergebnis in einem dritten Array müssen (also nicht arr1 verändern, da die anfängliche Frage zu erfordern schien), können Sie tun newArray = [... arr1, ... arr2]
dim
Oh, das wusste ich nicht. Vielen Dank. Ich habe einen Bearbeitungskommentar hinzugefügt.
Karel Bílek
Ähnlich wie Concat dann funktionieren würde, mit dem Bonus, persistent zu sein (Mutation des ursprünglichen Arrays).
Rob
@robertmylne Der Spread-Operator ändert offensichtlich nicht das ursprüngliche Array, sondern erstellt eine neue Kopie aller desparsierten Array-Inhalte.
Jack Giffin
145

Einen eleganten Weg von MDN gefunden

var vegetables = ['parsnip', 'potato'];
var moreVegs = ['celery', 'beetroot'];

// Merge the second array into the first one
// Equivalent to vegetables.push('celery', 'beetroot');
Array.prototype.push.apply(vegetables, moreVegs);

console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

Oder Sie können die spread operatorFunktion von ES6 verwenden:

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]
Believe2014
quelle
1
Dies ist nicht das, wonach die Frage gefragt hat. Die Frage lautet, wie Sie jedes Mal ein neues Array erstellen und kein altes Array ändern können.
Jack Giffin
13
var a=new Array('a','b','c');
var b=new Array('d','e','f');
var d=new Array('x','y','z');
var c=a.concat(b,d)

Löst das Ihr Problem?

Sébastien VINCENT
quelle
13

Folgendes scheint mir am einfachsten zu sein:

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);

Da "push" eine variable Anzahl von Argumenten akzeptiert, können Sie die applyMethode von verwendenpush Funktion verwenden, um alle Elemente eines anderen Arrays zu pushen. Es erstellt einen Aufruf zum Push mit seinem ersten Argument ("newArray" hier) als "this" und den Elementen des Arrays als verbleibenden Argumenten.

Die sliceAnweisung in der ersten Anweisung erhält eine Kopie des ersten Arrays, sodass Sie sie nicht ändern.

Update Wenn Sie eine Version von Javascript mit verfügbarem Slice verwenden, können Sie den pushAusdruck wie folgt vereinfachen :

newArray.push(...dataArray2)
shaunc
quelle
1
Wird auch hier bei MDN als Beispiel für "Zusammenführen von zwei Arrays" erwähnt
Wilt
12

Die folgende Funktion hat kein Problem mit der Länge der Arrays und bietet eine bessere Leistung als alle vorgeschlagenen Lösungen:

function pushArray(list, other) {
    var len = other.length;
    var start = list.length;
    list.length = start + len;
    for (var i = 0; i < len; i++ , start++) {
        list[start] = other[i];
    }
}

Leider weigert sich jspref, meine Einsendungen anzunehmen. Hier sind die Ergebnisse unter Verwendung von Benchmark.js

        Name            |   ops/sec   |  ± %  | runs sampled
for loop and push       |      177506 |  0.92 | 63
Push Apply              |      234280 |  0.77 | 66
spread operator         |      259725 |  0.40 | 67
set length and for loop |      284223 |  0.41 | 66

wo

für loop und push ist:

    for (var i = 0, l = source.length; i < l; i++) {
        target.push(source[i]);
    }

Drücken Sie Übernehmen:

target.push.apply(target, source);

Spread-Operator:

    target.push(...source);

und schließlich ist die 'eingestellte Länge und for-Schleife' die obige Funktion

Panos Theof
quelle
Diese Frage sucht nach einer Möglichkeit, jedes Mal ein neues Array zu erstellen, ohne ein vorhandenes Array zu ändern.
Jack Giffin
10

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗥𝗲𝘀𝘂𝗹𝘁𝘀

Für die Fakten werden ein Leistungstest bei jsperf und die Überprüfung einiger Dinge in der Konsole durchgeführt. Für die Recherche wird die Website irt.org verwendet. Unten finden Sie eine Sammlung all dieser Quellen sowie eine Beispielfunktion unten.

╔═══════════════╦══════╦═════════════════╦════════ ═══════╦═════════╦══════════╗
║ Methode ║Concat║slice & push.apply ║ push.apply x2 ║ ForLoop ║Spread ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ mOps / Sec ║179 ║104 ║ 76 ║ 81 ║28 ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Sparse Arrays ║ JA! »Nur die in Scheiben geschnittenen ║ nein ║ Vielleicht 2   ║ nein ║
║ spärlich gehalten ║ ║array (1. Argument) ║ ║ ║ ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║ Unterstützung ║MSIE 4║MSIE 5.5 ║ MSIE 5.5 ║ MSIE 4 ║Edge 12 ║
Source ( Quelle ) ║NNav 4║NNav 4.06 ║NNav 4.06 ║NNav 3 ║ MSIE  NNav ║
╠═══════════════╬══════╬═════════════════╬════════ ═══════╬═════════╬══════════╣
║Array-ähnliche Handlungen║nein theNur das Geschobene ║ JA! ║ JA! FWenn ║
║wie ein Array ║ ║array (2. Argument) ║ ║iterator 1   ║
╚═══════════════╩══════╩═════════════════╩════════ ═══════╩═════════╩══════════╝
1 Wenn das Array-ähnliche Objekt keine Symbol.iterator- Eigenschaft hat, versuchen Sie es
  um es zu verbreiten, wird eine Ausnahme ausgelöst.
2 Hängt vom Code ab. Der folgende Beispielcode "YES" bewahrt die Spärlichkeit.
function mergeCopyTogether(inputOne, inputTwo){
   var oneLen = inputOne.length, twoLen = inputTwo.length;
   var newArr = [], newLen = newArr.length = oneLen + twoLen;
   for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {
        tmp = inputOne[i];
        if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;
    }
    for (var two=0; i !== newLen; ++i, ++two) {
        tmp = inputTwo[two];
        if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;
    }
    return newArr;
}

Wie oben gezeigt, würde ich argumentieren, dass Concat fast immer der richtige Weg ist, um sowohl Leistung als auch die Fähigkeit zu erhalten, die Spärlichkeit von Ersatz-Arrays beizubehalten. Dann document.body.childrenwürde ich für Array-Likes (wie DOMNodeLists wie ) die Verwendung der for-Schleife empfehlen, da sie sowohl die zweitperformanteste als auch die einzige andere Methode ist, die spärliche Arrays beibehält. Im Folgenden werden wir schnell darauf eingehen, was unter spärlichen Arrays und Array-Likes zu verstehen ist, um Verwirrung zu beseitigen.

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

Zunächst denken einige Leute vielleicht, dass dies ein Zufall ist und dass Browser-Anbieter irgendwann dazu kommen werden, Array.prototype.push so zu optimieren, dass es schnell genug ist, um Array.prototype.concat zu schlagen. FALSCH! Array.prototype.concat ist immer schneller (zumindest im Prinzip), da es sich um ein einfaches Kopieren und Einfügen über die Daten handelt. Unten finden Sie ein vereinfachtes, überzeugend-visuelles Diagramm, wie eine 32-Bit-Array-Implementierung aussehen könnte (bitte beachten Sie, dass echte Implementierungen viel komplizierter sind).

Byte ║ Daten hier
═════╬═══════════
0x00 ║ int nonNumericPropertiesLength = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int Länge = 0x00000001
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueIndex = 0x00000000
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ int valueType = JS_PRIMITIVE_NUMBER
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid
0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (oder wo immer es sich im Speicher befindet)
0x01 ║ ibid
0x02 ║ ibid
0x03 ║ ibid

Wie oben gezeigt, ist alles, was Sie tun müssen, um so etwas zu kopieren, fast so einfach wie das Byte-für-Byte-Kopieren. Mit Array.prototype.push.apply ist es viel mehr als ein einfaches Kopieren und Einfügen über die Daten. Die ".apply" muss jeden Index im Array überprüfen und in eine Reihe von Argumenten konvertieren, bevor sie an Array.prototype.push übergeben wird. Dann muss Array.prototype.push jedes Mal zusätzlich mehr Speicher zuweisen und (für einige Browser-Implementierungen) möglicherweise sogar einige Positionssuchdaten für die Spärlichkeit neu berechnen.

Eine alternative Art, darüber nachzudenken, ist dies. Das Quellarray 1 ist ein großer Stapel zusammengehefteter Papiere. Das Quellarray zwei ist ebenfalls ein großer Papierstapel. Wäre es schneller für dich?

  1. Gehen Sie in den Laden und kaufen Sie genügend Papier, um eine Kopie jedes Quellarrays zu erhalten. Führen Sie dann jeden Papierstapel des Quellarrays durch ein Kopiergerät und heften Sie die resultierenden zwei Kopien zusammen.
  2. Gehen Sie in den Laden und kaufen Sie genug Papier für eine einzelne Kopie des ersten Quellarrays. Kopieren Sie dann das Quellarray von Hand auf das neue Papier, und achten Sie darauf, dass alle leeren Stellen mit geringer Dichte ausgefüllt werden. Gehen Sie dann zurück in den Laden und kaufen Sie genügend Papier für das zweite Quellarray. Gehen Sie dann das zweite Quellarray durch und kopieren Sie es, ohne dass Lücken in der Kopie entstehen. Heften Sie dann alle kopierten Papiere zusammen.

In der obigen Analogie steht Option 1 für Array.prototype.concat, während Option 2 für Array.prototype.push.apply steht. Lassen Sie uns dies mit einem ähnlichen JSperf testen, der sich nur darin unterscheidet, dass dieser die Methoden über spärliche Arrays und nicht über feste Arrays testet. Man kann es hier finden .

Daher ruhe ich meinen Fall aus, dass die Zukunft der Leistung für diesen speziellen Anwendungsfall nicht in Array.prototype.push liegt, sondern in Array.prototype.concat.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

Wenn bestimmte Mitglieder des Arrays einfach fehlen. Zum Beispiel:

// This is just as an example. In actual code, 
// do not mix different types like this.
var mySparseArray = [];
mySparseArray[0] = "foo";
mySparseArray[10] = undefined;
mySparseArray[11] = {};
mySparseArray[12] =  10;
mySparseArray[17] = "bar";
console.log("Length:   ", mySparseArray.length);
console.log("0 in it:  ", 0 in mySparseArray);
console.log("arr[0]:   ", mySparseArray[0]);
console.log("10 in it: ", 10 in mySparseArray);
console.log("arr[10]   ", mySparseArray[10]);
console.log("20 in it: ", 20 in mySparseArray);
console.log("arr[20]:  ", mySparseArray[20]);

Alternativ können Sie mit Javascript Ersatzarrays einfach initialisieren.

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

Ein Array-ähnliches Objekt ist ein Objekt, das mindestens eine lengthEigenschaft hat, jedoch nicht mit new Arrayoder initialisiert wurde []. Beispielsweise werden die folgenden Objekte als Array-ähnlich klassifiziert.

{0: "foo", 1: "bar", Länge: 2}
document.body.children
neues Uint8Array (3)
  • Dies ist Array-ähnlich, da das Konstruieren in ein Array den Konstruktor ändert, obwohl es sich um ein (n) (typisiertes) Array handelt.
(function () {Argumente zurückgeben}) ()

Beobachten Sie, was mit einer Methode passiert, die Array-Likes in Arrays wie Slice zwingt.

  • ANMERKUNG: Aufgrund der Leistung wird empfohlen, Slice-on-Funktionsargumente aufzurufen.

Beobachten Sie, was ein Verfahren geschieht verwenden , die sich nicht Array-likes in Arrays wie concat zwingen.

Jack Giffin
quelle
9

Mit JavaScript ES6 können Sie den Operator ... als Spread-Operator verwenden, der das Array im Wesentlichen in Werte konvertiert. Dann können Sie so etwas tun:

const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];

const newArray = [
  ...myArray,
  ...moreData,
];

Obwohl die Syntax kurz ist, weiß ich nicht, wie dies intern funktioniert und welche Auswirkungen dies auf die Leistung großer Arrays hat.

Ryan H.
quelle
2
Wenn Sie sich ansehen, wie Babel es konvertiert , werden Sie feststellen, dass es nicht langsamer sein sollte als mit Array.push.applyTechnik.
Emil.c
1
@JackGiffin Ich bezog mich nur auf das, was Ryan erwähnte, dass er nicht weiß, wie es intern funktioniert und was Auswirkungen auf die Leistung hat. Ich habe diesen Ansatz eigentlich nicht vorgeschlagen. Auf jeden Fall haben Sie Ihre Antwort sehr gut gemacht, gute Recherchen, es ist immer gut, solche Details zu kennen.
emil.c
9

Hier ist der ES6-Weg

var newArray = [];
let dataArray1 = [1,2,3,4]
let dataArray2 = [5,6,7,8]
newArray = [...dataArray1, ...dataArray2]
console.log(newArray)

Die oben beschriebene Methode eignet sich für die meisten Fälle, und die Fälle, die nicht berücksichtigt werden concat, sind, da Sie hunderttausende Elemente in Arrays haben.

    let dataArray1 = [1,2,3,4]
    let dataArray2 = [5,6,7,8]
    let newArray = dataArray1.concat(dataArray2);
    console.log(newArray)

Schwarze Mamba
quelle
8

Es gibt eine Reihe von Antworten zu Array.prototype.push.apply . Hier ist ein klares Beispiel:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]
Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Wenn Sie eine ES6-Syntax haben:

var dataArray1 = [1, 2];
var dataArray2 = [3, 4, 5];
var newArray = [ ];
newArray.push(...dataArray1); // newArray = [1, 2]
newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]
console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

Stephen Quan
quelle
4

Wenn Sie das ursprüngliche Array ändern möchten, können Sie Folgendes verbreiten und verschieben:

var source = [1, 2, 3];
var range = [5, 6, 7];
var length = source.push(...range);

console.log(source); // [ 1, 2, 3, 5, 6, 7 ]
console.log(length); // 6

Wenn Sie sicherstellen möchten, dass nur Elemente desselben Typs in das sourceArray aufgenommen werden (z. B. keine Zahlen und Zeichenfolgen mischen), verwenden Sie TypeScript.

/**
 * Adds the items of the specified range array to the end of the source array.
 * Use this function to make sure only items of the same type go in the source array.
 */
function addRange<T>(source: T[], range: T[]) {
    source.push(...range);
}
orad
quelle
2

Wir haben zwei Arrays a und b. Der Code, der hier ausgeführt wurde, ist Array. Ein Wert wird in Array b verschoben.

let a = [2, 4, 6, 8, 9, 15]

function transform(a) {
    let b = ['4', '16', '64']
    a.forEach(function(e) {
        b.push(e.toString());
    });
    return b;
}

transform(a)

[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]
KARTHIKEYAN.A
quelle
1
Bitte geben Sie nicht nur die Postleitzahl als Antwort ein. Erklären Sie, was der Code tut und wie er das Problem löst.
Patrick Hund
0

Versuche dies:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)
Trilok Singh
quelle
-2

Verwenden Sie anstelle der Funktion push () die Funktion concat für den IE. Beispiel,

var a=a.concat(a,new Array('amin'));
user1911703
quelle
1
beide sind sehr IE-kompatibel
Jack Giffin
-3

Dies ist ein funktionierender Code und es funktioniert gut:

var els = document.getElementsByTagName('input'), i;
var invnum = new Array();
var k = els.length;
for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}
user4354031
quelle