Javascript: inserisci un array all'interno di un altro array


91

Qual è il modo più efficiente per inserire un array all'interno di un altro array.

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

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

L'iterazione di a2 usando splice sembra un po 'terribile dal punto di vista delle prestazioni se l'array a2 è grande.

Grazie.



2
non è un elemento ma un array, quindi la giunzione non funziona
ic3

Risposte:


182

Puoi usare splicecombinato con qualche applytrucco:

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 +, potresti invece usare l'operatore spread per renderlo un po 'più carino

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

10
@icCube: ho appena eseguito un benchmark, confrontando questo metodo con quello nella risposta di Patrick. Inizia con a1 e a2 come nell'esempio e inietta la variabile a2 in a1 10.000 volte (in modo che alla fine si abbia un array con 20.005 elementi). Questo metodo ha richiesto: 81 ms , il metodo slice + concat ha richiesto 156 ms (testato su Chrome 13) . Ovviamente, questo viene fornito con l'avvertenza standard che nella maggior parte dei casi la leggibilità sarà più importante della velocità.
nickf

3
@nick: in chrome se gli array contengono oltre 130000 elementi, applyverrà generata un'eccezione stackoverflow . jsfiddle.net/DcHCY Succede anche in jsPerf Credo in FF e IE la soglia è di circa 500k
No

perché usi apply invece di chiamare splice direttamente e passare gli argomenti?
Peter P.

@ FrançoisWahl, questo è un problema serio che le persone spesso ignorano nella loro risposta. Ho preso il tuo esempio e l'ho fatto funzionare con il nord di 1 M di elementi: stackoverflow.com/a/41466395/1038326
Gabriel Kohen

L'ho appena provato (giugno 2017) e il modo più veloce sia per le dimensioni di array di piccole dimensioni che per le dimensioni di array di grandi dimensioni (su Chrome, FF e Edge) sembra essere target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima


14

All'inizio aveva sbagliato. Avrei dovuto usare concat()invece.

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

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

Questa espressione usa slice(0, 2)[docs] per restituire i primi due elementi di a1(dove 0è l'indice iniziale, ed 2è l'elemento deleteCount, sebbene a1non sia alterato).

Risultato intermedio :[1,2]

Quindi utilizza concat(a2)[docs] per aggiungere a2alla fine del file [1,2].

Risultato intermedio : [1,2,21,22].

Successivamente, a1.slice(2)viene chiamato all'interno di un finale .concat()all'estremità della coda di questa espressione, che equivale a [1,2,21,22].concat(a1.slice(2)).

Una chiamata a slice(2), con un argomento intero positivo, restituirà tutti gli elementi dopo il 2 ° elemento, contando per numeri naturali (come in, ci sono cinque elementi, quindi [3,4,5]verranno restituiti da a1). Un altro modo per dirlo è che l'argomento dell'indice intero singolare lo dicea1.slice() da quale posizione nell'array iniziare a restituire elementi (l'indice 2 è il terzo elemento).

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

Infine, il secondo si .concat()aggiunge [3,4,5]alla fine di[1,2,21,22] .

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

Potresti essere tentato di cambiare Array.prototype , ma puoi semplicemente estendere l'oggetto Array usando l'ereditarietà prototipale e iniettare il nuovo oggetto nei tuoi progetti.

Tuttavia, per chi vive al limite ...

Esempio: 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 );

Ottengo a1 con 6 elementi non 7 -> [1,2, [21,22], 3,4,5]
ic3

lavori ! grazie mille, aspettiamo qualche giorno ma questa è la migliore risposta ... strano non hanno una funzione js per questo.
ic3

Mi piace di più questo
Kimchi Man

In sintesi, il motore JS deve restituire quattro array prima che l'espressione sia terminata. Mi piace la logica della soluzione, ma potrebbe non essere ottimale in termini di considerazioni di spazio. Tuttavia sono d'accordo sul fatto che sia elegante e compatibile con tutti i browser. L'uso dell'operatore spread in quanto funziona sulle raccolte comporta un costo, ma a lungo termine potrei assumerlo per la restituzione di quattro array.
Anthony Rutledge

4

L' operatore spread consente di espandere un'espressione nei punti in cui sono previsti più argomenti (per chiamate di funzione) o più elementi (per valori letterali di matrice).

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


3

Volevo trovare un modo per farlo con splice()e senza iterazione: 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];

In forma di funzione generale:

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
}

Ciò modifica anche l' a2array, il che è probabilmente abbastanza indesiderabile. Inoltre, non è necessario andare a Array.prototypequando hai la funzione slice proprio lì su a1o a2.
nickf

Sapevo che stava modificando a2. OP può decidere se questo è un problema o meno. C'è qualcosa di sbagliato nell'andare al prototipo per ottenere il metodo? Mi sembrava più appropriato poiché volevo solo il metodo e l'oggetto veniva aggiunto nel fileapply() metodo.
jfriend00

Ebbene no, è la stessa identica funzione, ma una è più breve: Array.prototype.splicevsa1.splice :)
nickf

Aggiunta funzione di inserimento di array per scopi generali.
jfriend00

.concatrestituisce un nuovo array, non modifica quello esistente come splice. Dovrebbe essereargs = args.concat(arrayToInsert);
nickf

3

Ci sono alcune risposte veramente creative a questa domanda qui. Ecco una soluzione semplice per coloro che hanno appena iniziato con gli array. Se lo si desidera, può essere fatto funzionare fino ai browser compatibili con ECMAScript 3.

Impara qualcosa sulla giunzione prima di iniziare.

Rete di sviluppatori Mozilla: Array.prototype.splice ()

Innanzitutto, comprendi due importanti forme di .splice().

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

Metodo 1) Rimuovere gli elementi x (deleteCount), a partire da un indice desiderato.

let startIndex = 0, 
    deleteCount = 2;

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

Metodo 2) Rimuovere gli elementi dopo un indice iniziale desiderato fino alla fine della matrice.

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

Utilizzando .splice(), un obiettivo potrebbe essere quello di dividerea1 in array di testa e coda utilizzando una delle due forme sopra.

Utilizzando il metodo n. 1, il valore restituito diventerebbe la testa e a1la coda.

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

Ora, in un colpo solo, concatena la testa, il corpo ( a2) e la coda

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

Pertanto, questa soluzione è più simile al mondo reale di qualsiasi altra presentata finora. Non è quello che faresti con i Lego? ;-) Ecco una funzione, eseguita utilizzando il metodo # 2.

/**
*@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]

Più breve:

/**
*@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));
}

Più sicuro:

/**
*@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));
}

I vantaggi di questa soluzione includono:

1) Una semplice premessa domina la soluzione: riempire un array vuoto.

2) La nomenclatura di testa, corpo e coda sembra naturale.

3) Nessuna doppia chiamata a .slice() . Niente affettatura.

4) No .apply() . Altamente inutile.

5) Si evita il concatenamento dei metodi.

6) Funziona in ECMAScript 3 e 5 semplicemente usando al varposto di letoconst .

** 7) Assicura che ci saranno una testa e una coda da schiaffeggiare sul corpo, a differenza di molte altre soluzioni presentate. Se stai aggiungendo un array prima o dopo i limiti, dovresti almeno usare.concat() !!!!

Nota: l'uso dello spread opearator ...rende tutto questo molto più facile da realizzare.


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

Ecco la mia versione senza trucchi speciali:

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 questo caso, potrebbe essere più semplice .reverse () l'array new_values, invece di incrementare insert_index per ogni iterazione. Ovviamente, quando start_index è zero o start_index - 1, l'efficienza di questa soluzione è scarsa, paragonabile all'uso di .concat () senza alcun ciclo esplicito, o unshift () o push () `con .apply().
Anthony Rutledge

0

Se vuoi inserire un altro array in un array senza crearne uno nuovo, il modo più semplice è usare pusho unshiftconapply

Per esempio:

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

Questo funziona perché entrambi pushe unshiftaccettano un numero variabile di argomenti. Un bonus, puoi facilmente scegliere da quale estremità collegare l'array!


Solo facendo a1.concat(a2)o a2.concat(a1)ho escogitato qualcosa di più semplice di quello che suggerisci. Tuttavia, il problema è l'inserimento di un array tra i limiti dell'array, non l'aggiunta esclusiva di un array all'inizio o alla fine. ;-)
Anthony Rutledge

0

Come accennato in un altro thread, le risposte precedenti non funzioneranno in array molto grandi (200K elementi). Vedi la risposta alternativa qui che coinvolge giunzione e spinta manuale: 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]);
       }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.