¿Cómo insertar un elemento en una matriz en un índice específico (JavaScript)?

2932

Estoy buscando un método de inserción de matriz de JavaScript, en el estilo de:

arr.insert(index, item)

Preferiblemente en jQuery, pero cualquier implementación de JavaScript funcionará en este punto.

etiquetas2k
fuente
96
Tenga en cuenta que JQuery es una biblioteca de manipulación de eventos y DOM, no un lenguaje propio. No tiene nada que ver con la manipulación de matrices.
Domino
25
api.jquery.com/jQuery.inArray no tiene nada que ver con el DOM o los eventos. jQuery se ha convertido en un conjunto de herramientas mixto para el desarrollo JS del navegador, lo que lleva a las personas a esperar que tenga un método para todo.
Tim
44
@Tim, pero todavía no es un lenguaje propio (todavía hay algunas preguntas como "cómo sumar dos números en jQuery" aquí en SO)
Victor
3
@Victor No, y nunca lo será. jQuery fue útil y relevante, pero ha tenido su día.
Tim
1
Además, vea esto , para una sorpresa retorcida (:
noobie

Respuestas:

4757

Lo que desea es la splicefunción en el objeto de matriz nativa.

arr.splice(index, 0, item);se insertará itemen arrel índice especificado (eliminando 0elementos primero, es decir, es solo una inserción).

En este ejemplo crearemos una matriz y le agregaremos un elemento en el índice 2:

var arr = [];
arr[0] = "Jani";
arr[1] = "Hege";
arr[2] = "Stale";
arr[3] = "Kai Jim";
arr[4] = "Borge";

console.log(arr.join());
arr.splice(2, 0, "Lene");
console.log(arr.join());

tvanfosson
fuente
168
¡Gracias, pensé que me sentiría estúpido por preguntar, pero ahora que sé la respuesta, no lo sé! ¿Por qué demonios decidieron llamarlo empalme cuando un término más buscable era de uso común para la misma función?
tags2k
83
@ tags2k: ¿porque la función hace más que insertar elementos y su nombre ya se estableció en perl?
Christoph
55
El empalme puede insertarse, pero con la misma frecuencia no . Por ejemplo: arr.splice(2,3)eliminará 3 elementos comenzando en el índice 2. Sin pasar el 3er .... Enésimo parámetro no se inserta nada. Entonces el nombre insert()tampoco le hace justicia.
EBarr
14
Creo que el término "empalme" tiene sentido. Empalmar significa unirse o conectarse, también cambiar. Tiene una matriz establecida que ahora está "cambiando" que implicaría agregar o eliminar elementos. Usted especifica dónde comenzar en la matriz, luego cuántos elementos antiguos eliminar (si corresponde) y, por último, opcionalmente una lista de elementos nuevos para agregar. Splice también es un excelente término de ciencia ficción, por supuesto.
Jakub Keller
285

Puede implementar el Array.insertmétodo haciendo esto:

Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};

Entonces puedes usarlo como:

var arr = [ 'A', 'B', 'D', 'E' ];
arr.insert(2, 'C');

// => arr == [ 'A', 'B', 'C', 'D', 'E' ]
FrEsC 81
fuente
99
Para insertar varios elementos que puede usarArray.prototype.insert = function (index, items) { this.splice.apply(this, [index, 0].concat(items)); }
Ryan Smith,
77
El problema con agregar cosas a la matriz es que la función se mostrará como un elemento cuando lo haga para (i in arr) {...}
rep_movsd
77
Pero tenga en cuenta que no se recomienda extender los tipos nativos, ya que podría interferir con otro código o funcionalidad futura.
marsze
17
No modifique objetos que no le pertenecen
Luis Cabrera Benito
44
No modifique los prototipos
satya164
141

Además del empalme, puede usar este enfoque que no mutará la matriz original, pero creará una nueva matriz con el elemento agregado. Por lo general, debe evitar la mutación siempre que sea posible. Estoy usando el operador de propagación ES6 aquí.

const items = [1, 2, 3, 4, 5]

const insert = (arr, index, newItem) => [
  // part of the array before the specified index
  ...arr.slice(0, index),
  // inserted item
  newItem,
  // part of the array after the specified index
  ...arr.slice(index)
]

const result = insert(items, 1, 10)

console.log(result)
// [1, 10, 2, 3, 4, 5]

Esto se puede usar para agregar más de un elemento ajustando un poco la función para usar el operador de descanso para los nuevos elementos, y difundir eso también en el resultado devuelto

const items = [1, 2, 3, 4, 5]

const insert = (arr, index, ...newItems) => [
  // part of the array before the specified index
  ...arr.slice(0, index),
  // inserted items
  ...newItems,
  // part of the array after the specified index
  ...arr.slice(index)
]

const result = insert(items, 1, 10, 20)

console.log(result)
// [1, 10, 20, 2, 3, 4, 5]

gafi
fuente
1
¿Es esta una forma buena y segura de hacer esto? Pregunto porque esto parece muy elegante y conciso, pero ninguna otra respuesta toca esto. ¡La mayoría de ellos modifica el prototipo de objeto!
Harsh Kanchina
55
@HarshKanchina Eso es probablemente porque la mayoría de las respuestas son anteriores a ES6, pero este enfoque es muy común ahora desde mi experiencia
gafi
77

insertMétodos de matriz personalizados

1. Con múltiples argumentos y soporte de encadenamiento

/* Syntax:
   array.insert(index, value1, value2, ..., valueN) */

Array.prototype.insert = function(index) {
    this.splice.apply(this, [index, 0].concat(
        Array.prototype.slice.call(arguments, 1)));
    return this;
};

Puede insertar múltiples elementos (como lo splicehace el nativo ) y admite el encadenamiento:

["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(1, 6);
// ["b", "X", "Y", "Z", "c"]

2. Con soporte de fusión y encadenamiento de argumentos de tipo matriz

/* Syntax:
   array.insert(index, value1, value2, ..., valueN) */

Array.prototype.insert = function(index) {
    index = Math.min(index, this.length);
    arguments.length > 1
        && this.splice.apply(this, [index, 0].concat([].pop.call(arguments)))
        && this.insert.apply(this, arguments);
    return this;
};

Puede fusionar matrices de los argumentos con la matriz dada y también admite el encadenamiento:

["a", "b", "c", "d"].insert(2, "V", ["W", "X", "Y"], "Z").join("-");
// "a-b-V-W-X-Y-Z-c-d"

DEMO: http://jsfiddle.net/UPphH/

Visión
fuente
¿Existe una forma compacta de que esta versión también combine una matriz cuando encuentra una en los argumentos?
Nolo
No entiendo el primer resultado ["b", "X", "Y", "Z", "c"]. ¿Por qué no está "d"incluido? Me parece que si pones 6 como el segundo parámetro de slice()y hay 6 elementos en la matriz a partir del índice especificado, entonces deberías obtener los 6 elementos en el valor de retorno. (El documento dice howManypara ese parámetro). Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Alexis Wilke
En realidad, si uso un índice de 3 o más, no obtengo nada en la salida (caso 1., FireFox) ["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(3, 3);=>[ ]
Alexis Wilke
@AlexisWilke En el primer ejemplo usé slicemétodo y no splice, a lo que te refieres en el comentario. El segundo parámetro de slice(named end) es un índice basado en cero en el que finalizar la extracción. sliceextractos hasta pero no incluidosend . Por lo tanto, después de insertque tiene ["a", "b", "X", "Y", "Z", "c", "d"], de donde sliceextrae elementos con índices de 1hasta 6, es decir, de "b"hasta "d"pero no incluidos "d". ¿Tiene sentido?
VisioN
41

Si desea insertar varios elementos en una matriz a la vez, consulte esta respuesta de desbordamiento de pila: una mejor manera de empalmar una matriz en una matriz en javascript

También aquí hay algunas funciones para ilustrar ambos ejemplos:

function insertAt(array, index) {
    var arrayToInsert = Array.prototype.splice.apply(arguments, [2]);
    return insertArrayAt(array, index, arrayToInsert);
}

function insertArrayAt(array, index, arrayToInsert) {
    Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
    return array;
}

Finalmente, aquí hay un jsFiddle para que pueda verlo por usted mismo: http://jsfiddle.net/luisperezphd/Wc8aS/

Y así es como usas las funciones:

// if you want to insert specific values whether constants or variables:
insertAt(arr, 1, "x", "y", "z");

// OR if you have an array:
var arrToInsert = ["x", "y", "z"];
insertArrayAt(arr, 1, arrToInsert);
Luis perez
fuente
1
¿No sería mejor insertAt () llamar a insertArrayAt () una vez que ha creado un arrayToInsert de un solo elemento? Eso evita la repetición de código idéntico.
Matt Sach
1
este es un gran ejemplo de cuándo usar 'aplicar'
CRice
Agregué un parámetro removeCount a este método para aprovechar las ventajas de la capacidad de empalme para eliminar también elementos en ese índice: Array.prototype.splice.apply (array, [index, removeCount || 0] .concat (arrayToInsert));
CRice
23

Para fines de programación funcional y encadenamiento adecuados, Array.prototype.insert()es esencial una invención . En realidad, el empalme podría haber sido perfecto si hubiera devuelto la matriz mutada en lugar de una matriz vacía totalmente sin sentido. Así que aquí va

Array.prototype.insert = function(i,...rest){
  this.splice(i,0,...rest)
  return this
}

var a = [3,4,8,9];
document.write("<pre>" + JSON.stringify(a.insert(2,5,6,7)) + "</pre>");

Bien, lo anterior con el Array.prototype.splice()que muta la matriz original y algunos podrían quejarse como "no deberías modificar lo que no te pertenece" y eso podría resultar correcto también. Entonces, para el bienestar público, me gustaría dar otro Array.prototype.insert()que no mute la matriz original. Aquí va;

Array.prototype.insert = function(i,...rest){
  return this.slice(0,i).concat(rest,this.slice(i));
}

var a = [3,4,8,9],
    b = a.insert(2,5,6,7);
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));

Redu
fuente
2
"una matriz vacía totalmente sin sentido": solo devuelve una matriz vacía cuando el segundo parámetro es 0. Si es mayor que 0, devuelve los elementos eliminados de la matriz. Dado que está agregando a un prototipo y splicemuta la matriz original, no creo que la "programación funcional adecuada" pertenezca a ninguna parte cercana splice.
chrisbajorin
Estamos hablando de insertar aquí y el segundo parámetro de Array.prototype.splice () tiene que ser cero. Y lo que devuelve no tiene otro significado que "no he eliminado nada" y como lo usamos para insertar un elemento, ya tenemos esa información. Si no desea mutar la matriz original, puede hacer lo mismo con dos operaciones Array.prototype.slice () y una Array.prototype.concat (). Tu decides.
Reducido
1
Su segunda implementación es la más limpia de toda esta página y tiene cero votos. Por favor, toma el mío y sigue con el buen trabajo. (deberías evitar mutar el prototipo pero ya lo sabes)
NiKo
1
Creo que vale la pena mencionar que el parámetro rest es el nuevo ECMA 6th ( developer.mozilla.org/en/docs/Web/JavaScript/Reference/… )
Geza Turi
18

Recomiendo usar JavaScript puro en este caso, tampoco hay un método de inserción en JavaScript, pero tenemos un método que es un método Array incorporado que hace el trabajo por usted, se llama empalme ...

Veamos qué es splice () ...

El método splice () cambia el contenido de una matriz al eliminar elementos existentes y / o agregar nuevos elementos.

OK, imagina que tenemos esta matriz a continuación:

const arr = [1, 2, 3, 4, 5];

Podemos eliminar 3así:

arr.splice(arr.indexOf(3), 1);

Devolverá 3, pero si verificamos el arr ahora, tenemos:

[1, 2, 4, 5]

Hasta ahora, todo bien, pero ¿cómo podemos agregar un nuevo elemento a la matriz usando empalme? Pongamos de nuevo 3 en el arr ...

arr.splice(2, 0, 3);

Veamos que hemos hecho ...

Usamos el empalme nuevamente, pero esta vez para el segundo argumento, pasamos 0 , significa que no queremos eliminar ningún elemento, pero al mismo tiempo, agregamos un tercer argumento que es 3 que se agregará en el segundo índice ...

Debe tener en cuenta que podemos eliminar y agregar al mismo tiempo, por ejemplo, ahora podemos hacer:

arr.splice(2, 2, 3);

Lo cual eliminará 2 elementos en el índice 2, luego agregará 3 en el índice 2 y el resultado será:

[1, 2, 3, 5];

Esto muestra cómo funciona cada elemento en el empalme:

array.splice (inicio, deleteCount, item1, item2, item3 ...)

Alireza
fuente
13

Agregar elemento individual en un índice específico

//Append at specific position(here at index 1)
arrName.splice(1, 0,'newName1');
//1: index number, 0: number of element to remove, newName1: new element


//Append at specific position (here at index 3)
arrName[3] = 'newName1';

Agregar elementos múltiples en un índice específico

//Append from index number 1
arrName.splice(1, 0,'newElemenet1', 'newElemenet2', 'newElemenet3');
//1: index number from where append start, 
//0: number of element to remove, 
//newElemenet1,2,3: new elements
Srikrushna
fuente
8
Vale la pena notar que arrName [3] no se agrega, se anula.
sfratini
Agrega el elemento a la matriz existente que no se anula, Ej: let arrName = ['xxx', 'aaa', 'zzz']; arrName.splice (1, 0, 'aaa', 'bbb', 'ccc'); después de imprimir el NomArr
Srikrushna
Si arrName tiene más de 3 elementos, está anulando el tercero, no agregando. ¿O estoy mirando esto de la manera incorrecta?
sfratini
Si insertamos un elemento en el medio, desplaza el siguiente elemento sin anularlo. Por favor, especifique su problema, lo que necesita. Tome una matriz (ejemplo) y lo que necesita en la salida. por
favor comentame
1
@Srikrushna arrName[3] = 'newName1';se agregará si la matriz tiene solo 3 elementos. Si hay un elemento en el índice 3, este será reemplazado. Si desea agregar al final, es mejor usarloarrName.push('newName1');
temor el
6

Otra posible solución, con el uso de Array#reduce.

var arr = ["apple", "orange", "raspberry"],
    arr2 = [1, 2, 4];

function insert(arr, item, index) {
    arr = arr.reduce(function(s, a, i) {
      i == index ? s.push(item, a) : s.push(a);
      return s;
    }, []);   
    console.log(arr);
}

insert(arr, "banana", 1);
insert(arr2, 3, 2);

usuario amable
fuente
6

Aquí hay dos formas:

const array = [ 'My', 'name', 'Hamza' ];

array.splice(2, 0, 'is');

console.log("Method 1 : ", array.join(" "));

O

Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};

const array = [ 'My', 'name', 'Hamza' ];
array.insert(2, 'is');

console.log("Method 2 : ", array.join(" "));

M. Hamza Rajput
fuente
4

Aunque esto ya ha sido respondido, estoy agregando esta nota para un enfoque alternativo.

Quería colocar un número conocido de elementos en una matriz, en posiciones específicas, ya que provienen de una "matriz asociativa" (es decir, un objeto) que por definición no se garantiza que esté en un orden ordenado. Quería que la matriz resultante fuera una matriz de objetos, pero los objetos estarían en un orden específico en la matriz ya que una matriz garantiza su orden. Entonces hice esto.

Primero el objeto fuente, una cadena JSONB recuperada de PostgreSQL. Quería ordenarlo por la propiedad "orden" en cada objeto secundario.

var jsonb_str = '{"one": {"abbr": "", "order": 3}, "two": {"abbr": "", "order": 4}, "three": {"abbr": "", "order": 5}, "initialize": {"abbr": "init", "order": 1}, "start": {"abbr": "", "order": 2}}';

var jsonb_obj = JSON.parse(jsonb_str);

Como se conoce el número de nodos en el objeto, primero creo una matriz con la longitud especificada:

var obj_length = Object.keys(jsonb_obj).length;
var sorted_array = new Array(obj_length);

Y luego itere el objeto, colocando los objetos temporales recién creados en las ubicaciones deseadas en la matriz sin que realmente tenga lugar ninguna "clasificación".

for (var key of Object.keys(jsonb_obj)) {
  var tobj = {};
  tobj[key] = jsonb_obj[key].abbr;

  var position = jsonb_obj[key].order - 1;
  sorted_array[position] = tobj;
}

console.dir(sorted_array);
Ville
fuente
3

Cualquiera que todavía tenga problemas con este y haya probado todas las opciones anteriores y nunca lo haya obtenido. Estoy compartiendo mi solución, esto es para tener en cuenta que no desea indicar explícitamente las propiedades de su objeto frente a la matriz.

function isIdentical(left, right){
    return JSON.stringify(left) === JSON.stringify(right);
}

function contains(array, obj){
    let count = 0;
    array.map((cur) => {
          if(this.isIdentical(cur, obj)) count++;
    });
    return count > 0;
}

Esta es una combinación de iterar la matriz de referencia y compararla con el objeto que desea verificar, convertir ambos en una cadena y luego iterar si coincide. Entonces puedes contar. Esto se puede mejorar, pero aquí es donde me instalé. Espero que esto ayude.

Clyde
fuente
3

Aprovechando el método de reducción de la siguiente manera:

function insert(arr, val, index) {
    return index >= arr.length 
        ? arr.concat(val)
        : arr.reduce((prev, x, i) => prev.concat(i === index ? [val, x] : x), []);
}

Entonces, de esta manera podemos devolver una nueva matriz (será una forma funcional genial, mucho mejor que usar push o splice) con el elemento insertado en el índice, y si el índice es mayor que la longitud de la matriz, se insertará al final.

alejoko
fuente
3

Array#splice()es el camino a seguir, a menos que realmente desee evitar mutar la matriz. Dado 2 matrices arr1y arr2, he aquí cómo usted insertar el contenido de arr2dentro arr1después de que el primer elemento:

const arr1 = ['a', 'd', 'e'];
const arr2 = ['b', 'c'];

arr1.splice(1, 0, ...arr2); // arr1 now contains ['a', 'b', 'c', 'd', 'e']

console.log(arr1)

Si le preocupa la mutación de la matriz (por ejemplo, si usa Immutable.js), puede usar slice(), no confundir con splice()a 'p'.

const arr3 = [...arr1.slice(0, 1), ...arr2, ...arr1.slice(1)];
vkarpov15
fuente
2

¡Intenté esto y está funcionando bien!

var initialArr = ["India","China","Japan","USA"];
initialArr.splice(index, 0, item);

El índice es la posición donde desea insertar o eliminar el elemento. 0 es decir, el segundo parámetro define la cantidad de elementos del elemento que se eliminará del índice que son las nuevas entradas que desea hacer en la matriz. Puede ser uno o más de uno.

initialArr.splice(2, 0, "Nigeria");
initialArr.splice(2, 0, "Australia","UK");
Pawan
fuente
44
simplemente copie y pegue la respuesta anterior. Esto no agrega ningún valor a la pregunta. ya sea que agregue una nueva respuesta o comente una existente. Intenta contribuir con algo nuevo. no queremos estropear esta comunidad
hannad rehman
1

Aquí hay una función de trabajo que uso en una de mis aplicaciones.

Esto comprueba si el elemento sale

let ifExist = (item, strings = [ '' ], position = 0) => {
     // output into an array with empty string. Important just in case their is no item. 
    let output = [ '' ];
    // check to see if the item that will be positioned exist.
    if (item) {
        // output should equal to array of strings. 
        output = strings;
       // use splice in order to break the array. 
       // use positition param to state where to put the item
       // and 0 is to not replace an index. Item is the actual item we are placing at the prescribed position. 
        output.splice(position, 0, item);
    }
    //empty string is so we do not concatenate with comma or anything else. 
    return output.join("");
};

Y luego lo llamo a continuación.

ifExist("friends", [ ' ( ', ' )' ], 1)}  // output: ( friends )
ifExist("friends", [ ' - '], 1)}  // output:  - friends 
ifExist("friends", [ ':'], 0)}  // output:   friends: 
Venganza lesly
fuente
1

Un poco más antiguo, pero tengo que estar de acuerdo con Redu anterior porque el empalme definitivamente tiene una interfaz un poco confusa. Y la respuesta dada por cdbajorin de que "solo devuelve una matriz vacía cuando el segundo parámetro es 0. Si es mayor que 0, devuelve los elementos eliminados de la matriz" es, si bien exacto, prueba el punto. La intención de la función es empalmar o, como dijo anteriormente Jakob Keller, "unirse o conectarse, también cambiar. Tiene una matriz establecida que ahora está cambiando que implicaría agregar o eliminar elementos ..." Dado eso, el El valor de retorno de los elementos, si los hay, que se eliminaron es, en el mejor de los casos, incómodo. Y estoy 100% de acuerdo en que este método podría haber sido más adecuado para encadenar si hubiera devuelto lo que parece natural, una nueva matriz con los elementos empalmados agregados. Entonces podría hacer cosas como ["19", "17"]. Empalmar (1,0, "18"). Unirse ("...") o lo que quiera con la matriz devuelta. El hecho de que devuelva lo que se eliminó es simplemente una tontería en mi humilde opinión. Si la intención del método era "cortar un conjunto de elementos" y esa era su única intención, tal vez. Sin embargo, parece que si no sé lo que estoy cortando, probablemente tenga pocas razones para eliminar esos elementos, ¿no? Sería mejor si se comportara como concat, map, reduce, slice, etc., donde se crea una nueva matriz a partir de la matriz existente en lugar de mutar la matriz existente. Esos son todos encadenados, y ES un problema importante. Es bastante común en la manipulación de la matriz de cadena. Parece que el idioma necesita ir en una u otra dirección y tratar de mantenerlo lo más posible. Javascript es funcional y menos declarativo, solo parece una extraña desviación de la norma.

meem
fuente
-2

Actuación

Hoy (2020.04.24) realizo pruebas para soluciones elegidas para matrices grandes y pequeñas. Los probé en MacOs High Sierra 10.13.6 en Chrome 81.0, Safari 13.1, Firefox 75.0.

Conclusiones

Para todos los navegadores

  • Sorprendentemente, para los arreglos pequeños, las soluciones no in situ basadas en slicey reduce(D, E, F) suelen ser 10x-100x más rápidas que las soluciones in situ.
  • para grandes matrices, las soluciones en el lugar basadas en splice(AI, BI, CI) fueron más rápidas (a veces ~ 100x, pero depende del tamaño de la matriz)
  • para arreglos pequeños, la solución de BI fue más lenta
  • para grandes matrices, la solución E fue más lenta

ingrese la descripción de la imagen aquí

Detalles

Las pruebas se dividieron en dos grupos: soluciones en el lugar (AI, BI, CI) y soluciones no en el lugar (D, E, F) y se realizaron durante dos casos

  • prueba de matriz con 10 elementos: puedes ejecutarla AQUÍ
  • prueba de matriz con 1.000.000 de elementos: puedes ejecutarla AQUÍ

El código probado se presenta en el siguiente fragmento

Los resultados de ejemplo para una matriz pequeña en Chrome están a continuación

ingrese la descripción de la imagen aquí

Kamil Kiełczewski
fuente