Javascript - insérer un tableau dans un autre tableau

87

Quelle est la manière la plus efficace d'insérer un tableau dans un autre tableau.

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

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

Itérer a2 à l'aide de splice semble un peu mauvais du point de vue des performances si le tableau a2 est grand.

Merci.

ic3
la source
1
jetez un œil à stackoverflow.com/questions/586182/…
Endophage
2
ce n'est pas un élément mais un tableau, donc l'épissure ne fonctionne pas
ic3

Réponses:

177

Vous pouvez utiliser splicecombiné avec une applyastuce:

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

Dans ES2015 +, vous pouvez utiliser l'opérateur de diffusion à la place pour rendre cela un peu plus agréable

a1.splice(2, 0, ...a2);
nickf
la source
10
@icCube: Je viens de lancer un benchmark, en comparant cette méthode à celle de la réponse de Patrick. Il commence par a1 et a2 comme dans l'exemple, et injecte la variable a2 dans a1 10000 fois (de sorte qu'à la fin, vous ayez un tableau avec 20 005 éléments). Cette méthode a pris: 81ms , la méthode slice + concat a pris 156ms (testé sur Chrome 13) . Bien sûr, cela vient avec l'avertissement standard que dans la plupart des cas, la lisibilité sera plus importante que la vitesse.
nickf
3
@nick: dans Chrome, si les tableaux contiennent plus de 130000 éléments apply, une exception stackoverflow sera générée. jsfiddle.net/DcHCY Se produit également dans jsPerf Je crois en FF et IE le seuil est d'environ 500k
Non
pourquoi utilisez-vous apply plutôt que d'appeler directement splice et de passer les arguments?
Peter P.
@ FrançoisWahl, c'est un problème grave que les gens ignorent souvent dans leur réponse. J'ai pris votre exemple et je l'ai fait fonctionner avec au nord de 1M éléments: stackoverflow.com/a/41466395/1038326
Gabriel Kohen
Je viens d'essayer cela (juin 2017) et le moyen le plus rapide pour les petites tailles de tableaux et les grandes tailles de tableaux (sur Chrome, FF et Edge) semble être target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima
14

J'avais tort au début. J'aurais dû utiliser à la concat()place.

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

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

Cette expression utilise slice(0, 2)[docs] pour renvoyer les deux premiers éléments de a1(où 0est l'index de départ et 2est l'élément deleteCount, bien qu'il a1ne soit pas modifié).

Résultat intermédiaire :[1,2]

Il utilise ensuite concat(a2)[docs] pour ajouter a2à la fin du fichier [1,2].

Résultat intermédiaire : [1,2,21,22].

Ensuite, a1.slice(2)est appelé dans une fin .concat()à la fin de cette expression, ce qui équivaut à [1,2,21,22].concat(a1.slice(2)).

Un appel à slice(2), ayant un argument entier positif, retournera tous les éléments après le 2ème élément, en comptant par des nombres naturels (comme dans, il y a cinq éléments, donc [3,4,5]sera retourné à partir de a1). Une autre façon de dire cela est que l'argument d'index de nombre entier singulier indique a1.slice()à quelle position dans le tableau commencer à renvoyer des éléments (l'index 2 est le troisième élément).

Résultat intermédiaire :[1,2,21,22].concat([3,4,5])

Enfin, le second .concat()ajoute [3,4,5]à la fin de [1,2,21,22].

Résultat :[1,2,21,22,3,4,5]

Il peut être tentant de modifier Array.prototype, mais on peut simplement étendre l'objet Array en utilisant l'héritage prototypique et injecter ledit nouvel objet dans vos projets.

Cependant, pour ceux qui vivent à la limite ...

Exemple: 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 );
utilisateur113716
la source
J'obtiens a1 avec 6 éléments et non 7 -> [1,2, [21,22], 3,4,5]
ic3
travaux ! merci beaucoup, attendons quelques jours mais c'est la meilleure réponse ... bizarre, ils n'ont pas de fonction js pour cela.
ic3
J'aime mieux celui-ci
Kimchi Man
En somme, le moteur JS doit renvoyer quatre tableaux avant que l'expression ne soit terminée. J'aime la logique de la solution, mais elle n'est peut-être pas optimale en termes de considérations d'espace. Cependant, je conviens qu'il est astucieux et très compatible avec tous les navigateurs. Il y a un coût à utiliser l'opérateur de propagation car il fonctionne sur des collections, mais je pourrais prendre cela en plus de renvoyer quatre tableaux à long terme.
Anthony Rutledge
3

L' opérateur spread permet à une expression d'être développée aux endroits où plusieurs arguments (pour les appels de fonction) ou plusieurs éléments (pour les littéraux de tableau) sont attendus.

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

Hikmat Sijapati
la source
3

Il y a ici des réponses vraiment créatives à cette question. Voici une solution simple pour ceux qui débutent avec les tableaux. Il peut être conçu pour fonctionner jusqu'aux navigateurs compatibles ECMAScript 3, si vous le souhaitez.

Sachez quelque chose sur l'épissure avant de commencer.

Réseau de développeurs Mozilla: Array.prototype.splice ()

Tout d'abord, comprenez deux formes importantes de .splice().

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

Méthode 1) Supprimez les éléments x (deleteCount), à partir d'un index souhaité.

let startIndex = 0, 
    deleteCount = 2;

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

Méthode 2) Supprimez les éléments après un index de début souhaité jusqu'à la fin du tableau.

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

En utilisant .splice(), un objectif pourrait être de se diviser a1en tableaux tête et queue en utilisant l'une des deux formes ci-dessus.

En utilisant la méthode n ° 1, la valeur de retour deviendrait la tête et a1la queue.

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

Maintenant, d'un seul coup, concatène la tête, le corps ( a2) et la queue

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

Ainsi, cette solution ressemble plus au monde réel qu'à toute autre présentée jusqu'à présent. N'est-ce pas ce que vous feriez avec Legos? ;-) Voici une fonction, réalisée en utilisant la méthode # 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]

Plus court:

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

Plus sûr:

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

Les avantages de cette solution incluent:

1) Une simple prémisse domine la solution - remplir un tableau vide.

2) La nomenclature de la tête, du corps et de la queue semble naturelle.

3) Pas de double appel à .slice(). Aucun tranchage du tout.

4) Non .apply(). Très inutile.

5) Le chaînage des méthodes est évité.

6) Fonctionne dans ECMAScript 3 et 5 simplement en utilisant varau lieu de letou const.

** 7) Assure qu'il y aura une tête et une queue à claquer sur le corps, contrairement à de nombreuses autres solutions présentées. Si vous ajoutez un tableau avant ou après les limites, vous devriez au moins utiliser .concat()!!!!

Remarque: l'utilisation de l'opéateur d'étalement ...rend tout cela beaucoup plus facile à accomplir.

Anthony Rutledge
la source
2

Je voulais trouver un moyen de faire cela avec splice()et sans itération: 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];

Sous forme de fonction à usage général:

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
}
jfriend00
la source
Cela modifie également le a2tableau, ce qui n'est probablement pas souhaitable. De plus, vous n’avez pas besoin d’accéder au Array.prototypelorsque la fonction de tranche est activée a1ou a2.
nickf
J'étais conscient qu'il modifiait a2. OP peut décider si c'est un problème ou non. Y a-t-il quelque chose de mal à aller au prototype pour obtenir la méthode? Cela me semblait plus approprié puisque je voulais juste la méthode et que l'objet était ajouté à la apply()méthode.
jfriend00
Eh bien non, c'est exactement la même fonction, mais l'une est plus courte: Array.prototype.splicevs a1.splice:)
nickf
Ajout d'une fonction d'insertion de tableau à usage général.
jfriend00
.concatrenvoie un nouveau tableau, il ne modifie pas l'existant comme splice. Devrait êtreargs = 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);
Bhesh Gurung
la source
0

Voici ma version sans astuces spéciales:

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;
}
arlomedia
la source
Dans ce cas, il peut être plus simple de simplement .reverse () le tableau new_values, au lieu d'incrémenter insert_index pour chaque itération. Bien sûr, lorsque start_index vaut zéro ou start_index - 1, l'efficacité de cette solution est médiocre, comparée à l'utilisation de .concat () sans aucune boucle explicite, ou unshift () ou push () `with .apply().
Anthony Rutledge
0

Si vous souhaitez insérer un autre tableau dans un tableau sans créer un nouveau, le plus simple est d'utiliser soit pushou unshiftavecapply

Par exemple:

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

Cela fonctionne parce que les deux pushet unshiftprennent un nombre variable d'arguments. Un bonus, vous pouvez facilement choisir de quelle extrémité attacher le tableau!

RangerMauve
la source
Juste en faisant a1.concat(a2)ou a2.concat(a1)j'ai conçu quelque chose de plus facile que ce que vous suggérez. Cependant, le problème est l'insertion d'un tableau entre les limites du tableau, pas uniquement l'ajout d'un tableau au début ou à la fin. ;-)
Anthony Rutledge
0

Comme mentionné dans un autre fil de discussion, les réponses ci-dessus ne fonctionneront pas dans de très grands tableaux (éléments 200K). Voir la réponse alternative ici impliquant l'épissure et la poussée manuelle: 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]);
       }
    }
}
Gabriel Kohen
la source