Javascript - Fügen Sie ein Array in ein anderes Array ein


87

Was ist der effizientere Weg, um ein Array in ein anderes Array einzufügen?

a1 = [1,2,3,4,5];
a2 = [21,22];

newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];

Das Iterieren von a2 mithilfe von Spleiß sieht aus Sicht der Leistung etwas schrecklich aus, wenn das a2-Array groß ist.

Vielen Dank.



2
Es ist nicht ein Element, sondern ein Array, so dass Spleiß nicht funktioniert
ic3

Antworten:


176

Sie können splicemit einigen applyTricks kombiniert verwenden :

a1 = [1,2,3,4,5];
a2 = [21,22];

a1.splice.apply(a1, [2, 0].concat(a2));

console.log(a1); // [1, 2, 21, 22, 3, 4, 5];

In ES2015 + können Sie stattdessen den Spread-Operator verwenden, um dies ein bisschen angenehmer zu gestalten

a1.splice(2, 0, ...a2);

10
@icCube: Ich habe gerade einen Benchmark durchgeführt und diese Methode mit der in Patricks Antwort verglichen. Es beginnt wie im Beispiel mit a1 und a2 und fügt die Variable a2 10.000 Mal in a1 ein (so dass Sie am Ende ein Array mit 20.005 Elementen haben). Diese Methode dauerte: 81 ms , die Slice + Concat-Methode dauerte 156 ms (getestet auf Chrome 13) . Dies ist natürlich mit der üblichen Einschränkung verbunden, dass in den meisten Fällen die Lesbarkeit wichtiger ist als die Geschwindigkeit.
Nickf

3
@nick: Wenn die Arrays in Chrome mehr als 130000 Elemente enthalten, applywird eine Stackoverflow-Ausnahme ausgelöst. jsfiddle.net/DcHCY Kommt auch in jsPerf vor Ich glaube an FF und IE, der Schwellenwert liegt bei 500k
Nope

Warum verwenden Sie apply, anstatt splice direkt aufzurufen und die Argumente zu übergeben?
Peter P.

@ FrançoisWahl, dies ist ein ernstes Problem, das die Leute in ihrer Antwort oft ignorieren. Ich habe Ihr Beispiel genommen und es mit nördlich von 1M Elementen zum Laufen gebracht
Gabriel Kohen

Ich habe es gerade versucht (Juni 2017) und der schnellste Weg sowohl für kleine als auch für große target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); Arraygrößen
Alex zu sein Dima


14

Hatte es zuerst falsch. Hätte concat()stattdessen verwenden sollen.

var a1 = [1,2,3,4,5],
    a2 = [21,22],
    startIndex = 0,
    insertionIndex = 2,
    result;    

result = a1.slice(startIndex, insertionIndex).concat(a2).concat(a1.slice(insertionIndex));

Beispiel: http://jsfiddle.net/f3cae/1/

Dieser Ausdruck verwendet slice(0, 2)[docs] , um die ersten beiden Elemente von zurückzugeben a1(wobei der Startindex 0und 2das Element deleteCount sind, jedoch a1nicht geändert werden).

Zwischenergebnis :[1,2]

Anschließend werden mit concat(a2)[docs]a2 an das Ende des angehängt [1,2].

Zwischenergebnis : [1,2,21,22].

Als nächstes a1.slice(2)wird innerhalb eines Trailing .concat()am hinteren Ende dieses Ausdrucks aufgerufen , was gleichbedeutend ist [1,2,21,22].concat(a1.slice(2)).

Ein Aufruf von slice(2)mit einem positiven Ganzzahlargument gibt alle Elemente nach dem 2. Element zurück und zählt nach natürlichen Zahlen (wie in gibt es fünf Elemente, [3,4,5]wird also von zurückgegeben a1). Eine andere Möglichkeit, dies zu sagen, besteht darin, dass das singuläre Ganzzahlindexargument angibt, a1.slice()an welcher Position im Array Elemente zurückgegeben werden sollen (Index 2 ist das dritte Element).

Zwischenergebnis :[1,2,21,22].concat([3,4,5])

Schließlich .concat()fügt der zweite [3,4,5]das Ende von hinzu [1,2,21,22].

Ergebnis :[1,2,21,22,3,4,5]

Es mag verlockend sein, Änderungen vorzunehmen Array.prototype, aber Sie können das Array-Objekt einfach mithilfe der prototypischen Vererbung erweitern und das neue Objekt in Ihre Projekte einfügen.

Für diejenigen, die am Rande leben ...

Beispiel: http://jsfiddle.net/f3cae/2/

Array.prototype.injectArray = function( idx, arr ) {
    return this.slice( 0, idx ).concat( arr ).concat( this.slice( idx ) );
};

var a1 = [1,2,3,4,5];
var a2 = [21,22];

var result = a1.injectArray( 2, a2 );

Ich bekomme a1 mit 6 Gegenständen nicht 7 -> [1,2, [21,22], 3,4,5]
ic3

funktioniert! Vielen Dank, warten wir ein paar Tage, aber dies ist die beste Antwort ... seltsam, dass sie keine js-Funktion dafür haben.
ic3

Ich mag dieses besser
Kimchi Man

Insgesamt muss die JS-Engine vier Arrays zurückgeben, bevor der Ausdruck beendet ist. Ich mag die Logik der Lösung, aber sie ist aus Platzgründen möglicherweise nicht optimal. Ich stimme jedoch zu, dass es geschickt und sehr browserübergreifend kompatibel ist. Die Verwendung des Spread-Operators ist kostenpflichtig, da er für Sammlungen funktioniert. Auf lange Sicht könnte ich jedoch die Rückgabe von vier Arrays übernehmen.
Anthony Rutledge

3

Mit dem Spread- Operator kann ein Ausdruck an Stellen erweitert werden, an denen mehrere Argumente (für Funktionsaufrufe) oder mehrere Elemente (für Array-Literale) erwartet werden.

a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);


3

Hier gibt es einige wirklich kreative Antworten auf diese Frage. Hier ist eine einfache Lösung für diejenigen, die gerade erst mit Arrays beginnen. Auf Wunsch kann es bis zu ECMAScript 3-kompatiblen Browsern verwendet werden.

Wissen Sie etwas über Spleißen, bevor Sie beginnen.

Mozilla Developer Network: Array.prototype.splice ()

Verstehen Sie zunächst zwei wichtige Formen von .splice().

let a1 = [1,2,3,4],
    a2 = [1,2];

Methode 1) Entfernen Sie x (deleteCount) -Elemente ausgehend von einem gewünschten Index.

let startIndex = 0, 
    deleteCount = 2;

a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Methode 2) Entfernen Sie Elemente nach einem gewünschten Startindex bis zum Ende des Arrays.

a1.splice(2); // returns [3,4], a1 would be [1,2]

Mit Hilfe .splice()könnte ein Ziel zu spalten sein a1oben , indem Sie eine der beiden Formen in Kopf und Schwanz - Arrays.

Bei Verwendung der Methode 1 wird der Rückgabewert zum Kopf und a1zum Schwanz.

let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Verketten Sie nun auf einen Schlag Kopf, Körper ( a2) und Schwanz

[].concat(head, a2, a1);

Somit ähnelt diese Lösung mehr der realen Welt als jede andere, die bisher vorgestellt wurde. Würden Sie das nicht mit Legos machen? ;-) Hier ist eine Funktion, die mit Methode 2 durchgeführt wurde.

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    let tail = target.splice(startIndex); // target is now [1,2] and the head
    return [].concat(target, body, tail);
}

let newArray = insertArray([1, 2, 3, 4], ["a", "b"], 2); // [1, 2, "a", "b", 3, 4]

Kürzere:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    return [].concat(target, body, target.splice(startIndex));
}

Sicherer:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*@throws Error The value for startIndex must fall between the first and last index, exclusive.
*/
function insertArray(target, body, startIndex)
{
    const ARRAY_START = 0,
          ARRAY_END = target.length - 1,
          ARRAY_NEG_END = -1,
          START_INDEX_MAGNITUDE = Math.abs(startIndex);

    if (startIndex === ARRAY_START) {
        throw new Error("The value for startIndex cannot be zero (0).");
    }

    if (startIndex === ARRAY_END || startIndex === ARRAY_NEG_END) {
        throw new Error("The startIndex cannot be equal to the last index in target, or -1.");
    }

    if (START_INDEX_MAGNITUDE >= ARRAY_END) {
        throw new Error("The absolute value of startIndex must be less than the last index.");
    }

    return [].concat(target, body, target.splice(startIndex));
}

Die Vorteile dieser Lösung umfassen:

1) Eine einfache Prämisse dominiert die Lösung - füllen Sie ein leeres Array.

2) Die Nomenklatur von Kopf, Körper und Schwanz fühlt sich natürlich an.

3) Kein doppelter Anruf bei .slice(). Überhaupt kein Schneiden.

4) Nein .apply(). Sehr unnötig.

5) Methodenverkettung wird vermieden.

6) Funktioniert in ECMAScript 3 und 5 einfach mit oder varanstelle von letoder const.

** 7) Stellt sicher, dass im Gegensatz zu vielen anderen vorgestellten Lösungen Kopf und Schwanz auf den Körper geschlagen werden können. Wenn Sie ein Array vor oder nach den Grenzen hinzufügen, sollten Sie zumindest .concat()!!!!

Hinweis: Die Verwendung des Spread-Opearators ...erleichtert dies erheblich.


2

Ich wollte einen Weg finden, dies zu tun splice()und nicht zu iterieren: http://jsfiddle.net/jfriend00/W9n27/ .

a1 = [1,2,3,4,5];
a2 = [21,22];

a2.unshift(2, 0);          // put first two params to splice onto front of array
a1.splice.apply(a1, a2);   // pass array as arguments parameter to splice
console.log(a1);           // [1, 2, 21, 22, 3, 4, 5];

Im Allgemeinen Funktionsform:

function arrayInsertAt(destArray, pos, arrayToInsert) {
    var args = [];
    args.push(pos);                           // where to insert
    args.push(0);                             // nothing to remove
    args = args.concat(arrayToInsert);        // add on array to insert
    destArray.splice.apply(destArray, args);  // splice it in
}

Dies modifiziert auch das a2Array, was wahrscheinlich ziemlich unerwünscht ist. Außerdem müssen Sie nicht zu gehen, Array.prototypewenn Sie die Slice-Funktion direkt auf a1oder haben a2.
Nickf

Mir war bewusst, dass es a2 modifiziert. OP kann entscheiden, ob dies ein Problem ist oder nicht. Ist etwas falsch daran, zum Prototyp zu gehen, um die Methode zu erhalten? Es schien mir angemessener zu sein, da ich nur die Methode wollte und das Objekt in der apply()Methode hinzugefügt wurde .
jfriend00

Nun nein, es ist genau die gleiche Funktion, aber eine ist kürzer: Array.prototype.splicevs a1.splice:)
Nickf

Allzweck-Array-Einfügefunktion hinzugefügt.
jfriend00

.concatGibt ein neues Array zurück, es ändert das vorhandene nicht wie splice. Sollte seinargs = args.concat(arrayToInsert);
Nickf

0
var a1 = [1,2,3,4,5];
var a2 = [21,22];

function injectAt(d, a1, a2) {
    for(var i=a1.length-1; i>=d; i--) {
        a1[i + a2.length] = a1[i];
    }
    for(var i=0; i<a2.length; i++) {
        a1[i+d] = a2[i];
    }
}

injectAt(2, a1, a2);

alert(a1);

0

Hier ist meine Version ohne besondere Tricks:

function insert_array(original_array, new_values, insert_index) {
    for (var i=0; i<new_values.length; i++) {
        original_array.splice((insert_index + i), 0, new_values[i]);
    }
    return original_array;
}

In diesem Fall kann es einfacher sein, das Array new_values ​​einfach umzudrehen (), anstatt insert_index für jede Iteration zu erhöhen. Wenn start_index Null oder start_index - 1 ist, ist die Effizienz dieser Lösung natürlich schlecht, verglichen mit der Verwendung von .concat () ohne explizite Schleifen oder unshift () oder push () `with .apply().
Anthony Rutledge

0

Wenn Sie ein anderes Array in ein Array einfügen möchten, ohne ein neues zu erstellen, ist es am einfachsten, entweder pushoder unshiftmit zu verwendenapply

Z.B:

a1 = [1,2,3,4,5];
a2 = [21,22];

// Insert a1 at beginning of a2
a2.unshift.apply(a2,a1);
// Insert a1 at end of a2
a2.push.apply(a2,a1);

Dies funktioniert, weil beide pushund unshifteine variable Anzahl von Argumenten annehmen. Als Bonus können Sie ganz einfach auswählen, an welchem ​​Ende das Array angebracht werden soll!


Nur indem ich etwas einfacher mache a1.concat(a2)als a2.concat(a1)ich es vorschlage. Das Problem besteht jedoch darin, ein Array zwischen Array-Grenzen einzufügen und nicht ausschließlich ein Array am Anfang oder Ende hinzuzufügen. ;-)
Anthony Rutledge

0

Wie in einem anderen Thread erwähnt, funktionieren die obigen Antworten in sehr großen Arrays (200K-Elemente) nicht. Siehe alternative Antwort hier mit Spleiß und manuellem Push: https://stackoverflow.com/a/41465578/1038326

Array.prototype.spliceArray = function(index, insertedArray) {
    var postArray = this.splice(index);
    inPlacePush(this, insertedArray);
    inPlacePush(this, postArray);

    function inPlacePush(targetArray, pushedArray) {
  // Not using forEach for browser compatability
        var pushedArrayLength = pushedArray.length;
        for (var index = 0; index < pushedArrayLength; index++) {
           targetArray.push(pushedArray[index]);
       }
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.