1252 Stimmen

Kopieren von Array-Elementen in ein anderes Array

Ich habe ein JavaScript-Array dataArray die ich in ein neues Array schieben möchte newArray . Nur will ich nicht newArray[0] zu sein dataArray . Ich möchte alle Elemente in das neue Array schieben:

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 also alle Werte der einzelnen Daten-Arrays. Gibt es eine Kurzschrift wie pushValues zur Verfügung, so dass ich nicht jeden einzelnen Schritt durchlaufen muss. dataArray , indem Sie die Elemente nacheinander hinzufügen?

1611voto

WiseGuyEh Punkte 17620

Verwenden Sie die konkaten Funktion, etwa so:

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

Der Wert von newArray wird sein [1, 2, 3, 4] ( arrayA y arrayB bleiben unverändert; concat erstellt und gibt ein neues Array für das Ergebnis zurück).

890voto

Karel Bílek Punkte 34388

In ECMAScript 6 können Sie mit der Spreizsyntax :

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

console.log(arr1)

Die Spread-Syntax ist in allen gängigen Browsern verfügbar (mit Ausnahme von IE11). Für die aktuelle Kompatibilität, siehe dies (ständig aktualisiert) Kompatibilitätstabelle .

Siehe jedoch die Antwort von Jack Giffin weiter unten für weitere Kommentare zur Leistung. Es scheint concat ist immer noch besser und schneller als der Spread-Operator.

685voto

Tim Down Punkte 304837

Vorausgesetzt, Ihre Arrays sind nicht riesig (siehe Vorbehalt unten), können Sie die push() Methode des Arrays, an das Sie Werte anhängen möchten. push() kann mehrere Parameter annehmen, so dass Sie mit seiner apply() Methode, um das Array der zu verschiebenden Werte als Liste von Funktionsparametern zu übergeben. Dies hat den Vorteil gegenüber der Verwendung von concat() Elemente dem Array an Ort und Stelle hinzuzufügen, anstatt ein neues Array zu erstellen.

Allerdings scheint es, dass für große Arrays (in der Größenordnung von 100.000 Mitgliedern oder mehr), dieser Trick kann fehlschlagen . Für solche Arrays ist die Verwendung einer Schleife der bessere Ansatz. Siehe https://stackoverflow.com/a/17368101/96100 für Einzelheiten.

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

Vielleicht möchten Sie dies zu einer Funktion verallgemeinern:

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

... oder fügen Sie es zu Array Der Prototyp:

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

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

... oder das Original nachahmen push() Methode, indem sie mehrere Parameter zulässt und die Tatsache nutzt, dass concat() , wie push() erlaubt mehrere Parameter:

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, einschließlich IE <= 8, geeignet ist:

Array.prototype.pushArray = function() {
    var toPush = this.concat.apply([], arguments);
    for (var i = 0, len = toPush.length; i < len; ++i) {
        this.push(toPush[i]);
    }
};

165voto

Believe2014 Punkte 3784

Einen eleganten Weg gefunden von MDN

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 operator Funktion von ES6:

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

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

25voto

Jack G Punkte 3797

(für die ursprüngliche Frage)

Zu den Fakten: ein leistungsprüfung bei jsperf und die Überprüfung einiger Dinge in der Konsole werden durchgeführt. Für die Forschung, die Website irt.org verwendet wird. Nachfolgend finden Sie eine Zusammenstellung all dieser Quellen sowie eine Beispielfunktion am Ende der Seite.

 Method        Concatslice&push.apply  push.apply x2  ForLoop Spread    

 mOps/Sec      179   104               76             81      28        

 Sparse arrays YES!  Only the sliced   no             Maybe2  no        
 kept sparse         array (1st arg)                                    

 Support       MSIE 4MSIE 5.5          MSIE 5.5       MSIE 4  Edge 12   
 ([source](https://www.irt.org/xref/core_objects.htm))      NNav 4NNav 4.06         NNav 4.06      NNav 3  ~~MSIE~~ ~~NNav~~ 

Array-like actsno    Only the pushed   YES!           YES!    If have   
like an array        array (2nd arg)                          iterator1 

1 If the array-like object does not have a _Symbol.iterator_ property, then trying
  to spread it will throw an exception.
2 Depends on the code. The following example code "YES" preserves sparseness.

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 gesehen, würde ich argumentieren, dass Concat ist fast immer der Weg zu gehen, sowohl für die Leistung und die Fähigkeit, die Sparsamkeit der Ersatz-Arrays zu behalten. Dann, für Array-likes (wie DOMNodeLists wie document.body.children ), würde ich empfehlen, die for-Schleife zu verwenden, da sie sowohl die zweitleistungsfähigste als auch die einzige andere Methode ist, die spärliche Arrays beibehält. Im Folgenden werden wir kurz erläutern, was mit spärlichen Arrays und Array-ähnlichen Elementen gemeint ist, um Verwirrung zu vermeiden.

Zunächst mögen einige Leute denken, dass dies ein Zufall ist und dass die Browserhersteller 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 wird immer schneller sein (zumindest im Prinzip), weil es ein einfaches Kopieren und Einfügen der Daten ist. Nachfolgend sehen Sie ein vereinfachtes, visuelles Diagramm, wie eine 32-Bit-Array-Implementierung aussehen könnte (bitte beachten Sie, dass reale Implementierungen viel komplizierter sind)

Byte  Data here

0x00  int nonNumericPropertiesLength = 0x00000000
0x01  ibid
0x02  ibid
0x03  ibid
0x00  int length = 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 (or whereever it is in memory)
0x01  ibid
0x02  ibid
0x03  ibid

Wie oben gesehen, ist alles, was Sie tun müssen, um so etwas zu kopieren, fast so einfach wie das Kopieren von Byte für Byte. Mit Array.prototype.push.apply ist es viel mehr als ein einfaches Kopieren und Einfügen der Daten. Das ".apply" muss jeden Index im Array überprüfen und ihn in eine Reihe von Argumenten umwandeln, bevor es ihn an Array.prototype.push weitergibt. Dann muss Array.prototype.push zusätzlich jedes Mal mehr Speicher zuweisen und (bei einigen Browser-Implementierungen) vielleicht sogar einige Positions-Lookup-Daten für die Sparsamkeit neu berechnen.

Man kann sich das auch anders vorstellen. Der Quellbereich eins ist ein großer Stapel zusammengehefteter Papiere. Der Quellbereich zwei ist ebenfalls ein großer Stapel von Papieren. Wäre es schneller für Sie, wenn Sie

  1. Gehen Sie in den Laden und kaufen Sie so viel Papier, wie Sie für eine Kopie jeder Quellengruppe benötigen. Legen Sie dann die Papierstapel der einzelnen Quellen durch einen Kopierer und heften Sie die beiden Kopien zusammen.
  2. Gehen Sie in den Laden und kaufen Sie genügend Papier für eine einzige Kopie des ersten Quellenfeldes. Kopieren Sie dann das Quellfeld von Hand auf das neue Papier und füllen Sie dabei alle leeren Stellen aus. Dann gehst du zurück in den Laden und kaufst genügend Papier für die zweite Quelle. Gehen Sie dann die zweite Quelle durch und kopieren Sie sie, wobei Sie darauf achten, dass keine Lücken in der Kopie entstehen. Heften Sie dann alle kopierten Papiere zusammen.

In der obigen Analogie steht die Option 1 für Array.prototype.concat und die Option 2 für Array.prototype.push.apply. Testen wir dies mit einem ähnlichen JSperf, der sich nur dadurch unterscheidet, dass er die Methoden über sparse Arrays und nicht über solid Arrays testet. Man kann es finden genau hier .

Daher bin ich der Meinung, dass die Zukunft der Leistung für diesen speziellen Anwendungsfall nicht in Array.prototype.push, sondern in Array.prototype.concat liegt.

Wenn bestimmte Elemente des Arrays einfach nicht vorhanden sind. 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 auch einfach Ersatz-Arrays initialisieren.

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

-

Ein Array-like ist ein Objekt, das mindestens eine length Eigenschaft, wurde aber nicht initialisiert mit new Array o [] Zum Beispiel werden die folgenden Objekte als array-like eingestuft.

{0: "foo", 1: "bar", length:2}    document.body.children    new Uint8Array(3)
  • Dies ist Array-ähnlich, denn obwohl es sich um ein (n) (typisiertes) Array handelt, ändert sich der Konstruktor, wenn man ihn in ein Array zwingt.

    (function(){return arguments})()

Beobachten Sie, was passiert, wenn Sie eine Methode verwenden, die array-likes in arrays wie slice umwandelt.

var slice = Array.prototype.slice;
// For arrays:
console.log(slice.call(["not an array-like, rather a real array"]));
// For array-likes:
console.log(slice.call({0: "foo", 1: "bar", length:2}));
console.lg(slice.call(document.body.children));
console.log(slice.call(new Uint8Array(3)));
console.log(slice.call( function(){return arguments}() ));
  • HINWEIS: Der Aufruf von Slice auf Funktionsargumente ist aus Leistungsgründen eine schlechte Praxis.

Beobachten Sie, was passiert, wenn Sie eine Methode verwenden, die no Array-likes in Arrays wie concat umwandeln.

var empty = [];
// For arrays:
console.log(empty.concat(["not an array-like, rather a real array"]));
// For array-likes:
console.log(empty.concat({0: "foo", 1: "bar", length:2}));
console.log(empty.concat(document.body.children));
console.log(empty.concat(new Uint8Array(3)));
console.log(empty.concat( function(){return arguments}() ));

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