Javascript: inserta una matriz dentro de otra matriz

88

¿Cuál es la forma más eficiente de insertar una matriz dentro de otra matriz?

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

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

Iterar a2 usando empalme se ve un poco horrible desde el punto de vista del rendimiento si la matriz a2 es grande.

Gracias.

ic3
fuente
1
eche un vistazo a stackoverflow.com/questions/586182/…
Endophage
2
no es un elemento sino una matriz, por lo que el empalme no funciona
ic3

Respuestas:

180

Puedes usar splicecombinado con algunos applytrucos:

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

En ES2015 +, podría usar el operador de propagación en su lugar para hacer esto un poco más agradable

a1.splice(2, 0, ...a2);
nickf
fuente
10
@icCube: Acabo de ejecutar un punto de referencia, comparando este método con el de la respuesta de Patrick. Comienza con a1 y a2 como en el ejemplo, e inyecta la variable a2 en a1 10,000 veces (de modo que al final, tiene una matriz con 20,005 elementos). Este método tomó: 81ms , el método slice + concat tomó 156ms (probado en Chrome 13) . Por supuesto, esto viene con la advertencia estándar de que, en la mayoría de los casos, la legibilidad será más importante que la velocidad.
nickf
3
@nick: en Chrome, si las matrices contienen más de 130000 elementos, se applyproducirá una excepción de desbordamiento de pila . jsfiddle.net/DcHCY También ocurre en jsPerf Creo en FF e IE el umbral es de alrededor de 500k
No.
¿Por qué usa aplicar en lugar de llamar a splice directamente y pasar los argumentos?
Peter P.
@ FrançoisWahl, este es un tema serio que la gente a menudo ignora en su respuesta. Tomé su ejemplo y lo hice funcionar con elementos al norte de 1M: stackoverflow.com/a/41466395/1038326
Gabriel Kohen
Acabo de probar esto (junio de 2017) y la forma más rápida para tamaños de matriz pequeños y tamaños de matriz grandes (en Chrome, FF y Edge) parece ser target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima
14

Me equivoqué al principio. Debería haber usado en su concat()lugar.

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

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

Esta expresión usa slice(0, 2)[docs] para devolver los dos primeros elementos de a1(donde 0es el índice inicial y 2es el elemento deleteCount, aunque a1no se modifica).

Resultado intermedio :[1,2]

Luego usa concat(a2)[docs] para agregar a2al final del [1,2].

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

A continuación, a1.slice(2)se llama dentro de un final .concat()al final de esta expresión, que equivale a [1,2,21,22].concat(a1.slice(2)).

Una llamada a slice(2), que tiene un argumento entero positivo, devolverá todos los elementos después del segundo elemento, contando por números naturales (como en, hay cinco elementos, por [3,4,5]lo que se devolverá desde a1). Otra forma de decir esto es que el argumento de índice entero singular indica a1.slice()en qué posición de la matriz comenzar a devolver elementos (el índice 2 es el tercer elemento).

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

Finalmente, el segundo se .concat()suma [3,4,5]al final de [1,2,21,22].

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

Puede ser tentador modificarlo Array.prototype, pero uno puede simplemente extender el objeto Array usando herencia prototípica e inyectar dicho nuevo objeto en sus proyectos.

Sin embargo, para aquellos que viven al límite ...

Ejemplo: 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 );
usuario113716
fuente
Obtengo a1 con 6 elementos, no 7 -> [1,2, [21,22], 3,4,5]
ic3
trabajos ! muchas gracias, esperemos algunos días, pero esta es la mejor respuesta ... raro, no tienen una función js para esto.
ic3
Me gusta más este
Kimchi Man
En resumen, el motor JS debe devolver cuatro matrices antes de que finalice la expresión. Me gusta la lógica de la solución, pero puede que no sea óptima en términos de consideraciones de espacio. Sin embargo, estoy de acuerdo en que es ingenioso y muy compatible con todos los navegadores. Hay un costo por usar el operador de propagación, ya que funciona en colecciones, pero podría asumirlo antes de devolver cuatro matrices a largo plazo.
Anthony Rutledge
3

El operador de extensión permite expandir una expresión en lugares donde se esperan múltiples argumentos (para llamadas a funciones) o múltiples elementos (para matrices literales).

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

Hikmat Sijapati
fuente
3

Aquí hay algunas respuestas verdaderamente creativas a esta pregunta. Aquí hay una solución simple para aquellos que recién comienzan con matrices. Se puede hacer que funcione hasta los navegadores compatibles con ECMAScript 3, si lo desea.

Sepa algo sobre empalmes antes de comenzar.

Red de desarrolladores de Mozilla: Array.prototype.splice ()

Primero, comprenda dos formas importantes de .splice().

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

Método 1) Elimine los elementos x (deleteCount), comenzando desde un índice deseado.

let startIndex = 0, 
    deleteCount = 2;

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

Método 2) Eliminar elementos después de un índice de inicio deseado hasta el final de la matriz.

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

Al usar .splice(), un objetivo podría ser dividir a1en matrices de cabeza y cola mediante uno de los dos formularios anteriores.

Usando el método n. ° 1, el valor de retorno se convertiría en la cabeza y a1la cola.

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

Ahora, de un solo golpe, concatenar la cabeza, el cuerpo ( a2) y la cola

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

Por lo tanto, esta solución se parece más al mundo real que cualquier otra presentada hasta ahora. ¿No es esto lo que harías con Legos? ;-) Aquí hay una función, hecha usando el método # 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]

Corta:

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

Más seguro:

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

Las ventajas de esta solución incluyen:

1) Una premisa simple domina la solución: llene una matriz vacía.

2) La nomenclatura de la cabeza, el cuerpo y la cola se siente natural.

3) No hay doble llamada a .slice(). Sin rebanar en absoluto.

4) No .apply(). Altamente innecesario.

5) Se evita el encadenamiento de métodos.

6) Funciona en ECMAScript 3 y 5 simplemente usando en varlugar de leto const.

** 7) Asegura que habrá una cabeza y una cola para golpear el cuerpo, a diferencia de muchas otras soluciones presentadas. Si está agregando una matriz antes o después de los límites, al menos debería usar .concat()!!!!

Nota: El uso del operador de propagación ...hace que todo esto sea mucho más fácil de lograr.

Anthony Rutledge
fuente
2

Quería encontrar una manera de hacer esto splice()sin iterar: 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];

En forma de función de propósito general:

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
fuente
Esto también modifica la a2matriz, lo que probablemente sea bastante indeseable. Además, no es necesario que vaya a Array.prototypecuando tenga la función de corte allí mismo en a1o a2.
nickf
Sabía que estaba modificando a2. OP puede decidir si eso es un problema o no. ¿Hay algo de malo en ir al prototipo para obtener el método? Me pareció más apropiado ya que solo quería el método y el objeto se estaba agregando en el apply()método.
jfriend00
Bueno, no, es exactamente la misma función, pero una es más corta: Array.prototype.splicevs a1.splice:)
nickf
Se agregó la función de inserción de matriz de propósito general.
jfriend00
.concatdevuelve una nueva matriz, no modifica la existente como splice. Debería serargs = 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
fuente
0

Aquí está mi versión sin trucos especiales:

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
fuente
En este caso, puede ser más sencillo simplemente .reverse () la matriz new_values, en lugar de incrementar insert_index para cada iteración. Por supuesto, cuando start_index es cero o start_index - 1, la eficiencia de esta solución es pobre, en comparación con usar .concat () sin ningún bucle explícito, o unshift () o push () `with .apply().
Anthony Rutledge
0

Si desea insertar otra matriz en una matriz sin crear una nueva, la forma más fácil es usar pusho unshiftconapply

P.ej:

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

Esto funciona porque ambos pushy unshifttoman un número variable de argumentos. ¡Una ventaja adicional es que puede elegir fácilmente desde qué extremo adjuntar la matriz!

RangerMauve
fuente
Con solo hacer a1.concat(a2)o a2.concat(a1)he ideado algo más fácil de lo que sugieres. Sin embargo, el problema es insertar una matriz entre los límites de la matriz, no agregar exclusivamente una matriz al principio o al final. ;-)
Anthony Rutledge
0

Como se mencionó en otro hilo, las respuestas anteriores no funcionarán en matrices muy grandes (elementos de 200K). Vea aquí una respuesta alternativa que involucra empalme y empuje manual: 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
fuente