Eliminar varios elementos de la matriz en Javascript / jQuery

117

Tengo dos matrices. La primera matriz contiene algunos valores, mientras que la segunda matriz contiene índices de los valores que deben eliminarse de la primera matriz. Por ejemplo:

var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

Quiero eliminar los valores presentes en los índices 0,2,4de valuesArr. Pensé que el splicemétodo nativo podría ayudar, así que se me ocurrió:

$.each(removeValFromIndex,function(index,value){
    valuesArr.splice(value,1);
});

Pero no funcionó porque después de cada uno splice, los índices de los valores valuesArreran diferentes. Podría resolver este problema usando una matriz temporal y copiando todos los valores en la segunda matriz, pero me preguntaba si existen métodos nativos a los que podamos pasar múltiples índices en los que eliminar valores de una matriz.

Preferiría una solución jQuery. (No estoy seguro si puedo usar grepaquí)

xyz
fuente

Respuestas:

256

Siempre está el viejo forciclo simple :

var valuesArr = ["v1","v2","v3","v4","v5"],
    removeValFromIndex = [0,2,4];    

for (var i = removeValFromIndex.length -1; i >= 0; i--)
   valuesArr.splice(removeValFromIndex[i],1);

Continúe removeValFromIndexen orden inverso y podrá .splice()hacerlo sin estropear los índices de los elementos que aún no se han eliminado.

Tenga en cuenta que en lo anterior he usado la sintaxis literal de matriz con corchetes para declarar las dos matrices. Esta es la sintaxis recomendada porque el new Array()uso es potencialmente confuso dado que responde de manera diferente según la cantidad de parámetros que ingrese.

EDITAR : Acabo de ver su comentario sobre otra respuesta sobre la matriz de índices que no necesariamente está en ningún orden en particular. Si ese es el caso, simplemente ordénelo en orden descendente antes de comenzar:

removeValFromIndex.sort(function(a,b){ return b - a; });

Y siga eso con cualquier $.each()método de bucle / / etc. que desee.

nnnnnn
fuente
1
no desordenaré el índice
Muhammad Umer
5
@MuhammadUmer - No, no si lo haces correctamente, que es lo que explica mi respuesta.
nnnnnn
5
Gracias por el conocimiento del orden inverso.
Daniel Nalbach
2
+1, no me di cuenta de que tenía que hacer el empalme en orden inverso, aunque en lugar de usar forEach, mi enfoque es usar$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel
1
esto solo funcionará si removeValFromIndex está ordenado en orden ascendente
Kunal Burangi
24

Aquí hay uno que uso cuando no voy con lodash / subrayado:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}
Dan Ochiana
fuente
¡Solución elegante! Al principio pensé que esto no funcionaría porque pensé que cada vez que llames slicetendrías que volver a calcular los índices que se eliminarían (-1 en IndexestoBeRemoved), ¡pero en realidad funciona!
Renato Gama
2
Demasiado inteligente
Farzad YZ
11
Esta solución funciona si solo la IndexesToBeRemovedmatriz se ordena en forma ascendente.
xfg
Estos índices se invalidarán después del primer empalme.
shinzou
@shinzou: no si IndexesToBeRemovedestá ordenado (ascendente).
nnnnnn
18

No, in-placepero se puede hacer usando grepy inArrayfunciones de jQuery.

var arr = $.grep(valuesArr, function(n, i) {
    return $.inArray(i, removeValFromIndex) ==-1;
});

alert(arr);//arr contains V2, V4

mira este violín.

TheVillageIdiot
fuente
Estaría (lo suficientemente cerca de) en el lugar si lo valuesArr = $.grep(...);
dijeras
1
@nnnnnn ja ja ja Me iba a una reunión (sabes que te hacen tan productivo) así que no experimenté mucho.
TheVillageIdiot
1
@ cept0 ¿Por qué el voto negativo? OP pidió una solución jQuery.
Ste77
Muchas gracias! @TheVillageIdiot
ecorvo
jsfiddler - Error 404. Lo sentimos mucho, pero no existe tal página.
Ash
17

Le sugiero que use Array.prototype.filter

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];
valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
})
Саша Давиденко
fuente
indexOf dentro del filtro ... no óptimo
Alvaro Joao
1
voto a favor. más rápido que el método de empalme según jsperf.com/remove-multiple/1
lionbigcat
¡Excelente! Ahora puedo dormir :)
Firmansyah
7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

La referencia de MDN está aquí

riship89
fuente
@ riship89 The fiddle is down
mate64
@Karna: violín caído
riship89
1
Tenga en cuenta que en el momento de escribir este artículo (junio de 2014), Array.prototype.find es parte del borrador actual de ES6 y solo se implementó en el Firefox
Olli K
6

En JS puro, puede recorrer la matriz hacia atrás, por splice()lo que no estropeará los índices de los elementos siguientes en el ciclo:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}
Watchduck
fuente
No funciona, puaj no es una función, así que se supone que es el índice de índices de elementos no deseados, y puaj usado [arr [i]]
DavChana
5

Se siente necesario publicar una respuesta con O(n)tiempo :). El problema con la solución de empalme es que debido a que la implementación subyacente de la matriz es literalmente una matriz , cada splicellamada llevará O(n)tiempo. Esto es más pronunciado cuando configuramos un ejemplo para explotar este comportamiento:

var n = 100
var xs = []
for(var i=0; i<n;i++)
  xs.push(i)
var is = []
for(var i=n/2-1; i>=0;i--)
  is.push(i)

Esto elimina elementos comenzando desde el medio hasta el inicio, por lo tanto, cada eliminación obliga al motor js a copiar n/2elementos, tenemos (n/2)^2operaciones de copia en total que son cuadráticas.

La solución de empalme (suponiendo isque ya esté ordenada en orden decreciente para deshacerse de los gastos generales) es la siguiente:

for(var i=0; i<is.length; i++)
  xs.splice(is[i], 1)

Sin embargo, no es difícil implementar una solución de tiempo lineal, reconstruyendo la matriz desde cero, usando una máscara para ver si copiamos elementos o no (sort empujará esto a O(n)log(n)). La siguiente es una implementación de este tipo (no maskes que sea ​​booleano invertido para la velocidad):

var mask = new Array(xs.length)
for(var i=is.length - 1; i>=0; i--)
  mask[is[i]] = true
var offset = 0
for(var i=0; i<xs.length; i++){
  if(mask[i] === undefined){
    xs[offset] = xs[i]
    offset++
  }
}
xs.length = offset

Ejecuté esto en jsperf.com e incluso n=100el método de empalme es un 90% más lento. Para mayor nesta diferencia será mucho mayor.

Simonzack
fuente
5

Quick ES6 one liner:

const valuesArr = new Array("v1","v2","v3","v4","v5");   
const removeValFromIndex = new Array(0,2,4);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => removeValFromIndex.includes(i))
nevace
fuente
Su código debería ejecutarse más rápido si crea removeValFromIndexun Set()y usa en removeValFromIndex.haslugar de includes.
Boris
5

Una solución simple y eficiente (complejidad lineal) que usa filter y Set :

const valuesArr = ['v1', 'v2', 'v3', 'v4', 'v5'];   
const removeValFromIndex = [0, 2, 4];

const indexSet = new Set(removeValFromIndex);

const arrayWithValuesRemoved = valuesArr.filter((value, i) => !indexSet.has(i));

console.log(arrayWithValuesRemoved);

La gran ventaja de esa implementación es que la operación ( hasfunción) de búsqueda Establecer toma un tiempo constante, siendo más rápida que la respuesta de nevace, por ejemplo.

Alberto Trindade Tavares
fuente
@MichaelPaccione Encantado de ayudar :)
Alberto Trindade Tavares
3

Esto funciona bien para mí y también funciona al eliminar de una matriz de objetos:

var array = [ 
    { id: 1, name: 'bob', faveColor: 'blue' }, 
    { id: 2, name: 'jane', faveColor: 'red' }, 
    { id: 3, name: 'sam', faveColor: 'blue' }
];

// remove people that like blue

array.filter(x => x.faveColor === 'blue').forEach(x => array.splice(array.indexOf(x), 1));

Puede haber una forma más corta y eficiente de escribir esto, pero funciona.

StuartMc
fuente
2

Una solución simple con ES5. Esto parece más apropiado para la mayoría de las aplicaciones hoy en día, ya que muchas ya no quieren depender de jQuery, etc.

Cuando los índices que se eliminarán se ordenan en orden ascendente:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [0, 2, 4]; // ascending

removeValFromIndex.reverse().forEach(function(index) {
  valuesArr.splice(index, 1);
});

Cuando los índices que se eliminarán no están ordenados:

var valuesArr = ["v1", "v2", "v3", "v4", "v5"];   
var removeValFromIndex = [2, 4, 0];  // unsorted

removeValFromIndex.sort(function(a, b) { return b - a; }).forEach(function(index) {
  valuesArr.splice(index, 1);
});
Kaspar Fenner
fuente
1

Puede corregir su código reemplazando removeValFromIndexcon removeValFromIndex.reverse(). Si no se garantiza que esa matriz use orden ascendente, puede usar removeValFromIndex.sort(function(a, b) { return b - a }).

minopret
fuente
Me parece bien: jsfiddle.net/mrtsherman/gDcFu/2 . Aunque esto supone la suposición de que la lista de eliminación está en orden.
mrtsherman
@minopret: Gracias, pero solo funcionará si los índices de removeValFromIndexestán en orden ascendente.
xyz
1

Si está usando underscore.js , puede usarlo _.filter()para resolver su problema.

var valuesArr = new Array("v1","v2","v3","v4","v5");
var removeValFromIndex = new Array(0,2,4);
var filteredArr = _.filter(valuesArr, function(item, index){
                  return !_.contains(removeValFromIndex, index);
                });

Además, si está intentando eliminar elementos utilizando una lista de elementos en lugar de índices, simplemente puede usar _.without(), así:

var valuesArr = new Array("v1","v2","v3","v4","v5");
var filteredArr = _.without(valuesArr, "V1", "V3");

Ahora filteredArrdebería ser["V2", "V4", "V5"]

Johnny Zhao
fuente
Cómo se implementa contiene en guión bajo ... tenga cuidado si es equivalente a indexOf dentro del filtro ... no es óptimo en absoluto ...
Alvaro Joao
1

filter + indexOf (IE9 +):

function removeMany(array, indexes) {
  return array.filter(function(_, idx) {
    return indexes.indexOf(idx) === -1;
  });
}); 

O con el filtro ES6 + buscar (Edge +):

function removeMany(array, indexes = []) {
  return array.filter((_, idx) => indexes.indexOf(idx) === -1)
}
daviestar
fuente
indexOf dentro del filtro ... no óptimo
Alvaro Joao
1

Aquí tienes un rapidito.

function removeFromArray(arr, toRemove){
    return arr.filter(item => toRemove.indexOf(item) === -1)
}

const arr1 = [1, 2, 3, 4, 5, 6, 7]
const arr2 = removeFromArray(arr1, [2, 4, 6]) // [1,3,5,7]
Merrick Kavolsky
fuente
indexOf dentro del filtro ... no óptimo
Alvaro Joao
0

Parece que Aplicar podría ser lo que estás buscando.
tal vez algo como esto funcionaría?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );
Robar
fuente
Pero el .splice()método no espera una lista de elementos para eliminar, espera un índice único del elemento en el que comenzar a eliminar seguido del número de elementos a eliminar ...
nnnnnn
0

Para artículos múltiples o artículo único:

Le sugiero que use Array.prototype.filter

¡Nunca use indexOf si ya conoce el índice !:

var valuesArr = ["v1","v2","v3","v4","v5"];
var removeValFrom = [0, 2, 4];

valuesArr = valuesArr.filter(function(value, index) {
     return removeValFrom.indexOf(index) == -1;
}); // BIG O(N*m) where N is length of valuesArr and m is length removeValFrom

Hacer:

con Hashes ... usando Array.prototype.map

  var valuesArr = ["v1","v2","v3","v4","v5"];
  var removeValFrom = {};
  ([0, 2, 4]).map(x=>removeValFrom[x]=1); //bild the hash.
  valuesArr = valuesArr.filter(function(value, index) {
      return removeValFrom[index] == 1;
  }); // BIG O(N) where N is valuesArr;
Álvaro Joao
fuente
0
var valuesArr = new Array("v1","v2","v3","v4","v5");   
var removeValFromIndex = new Array(0,2,4);

console.log(valuesArr)
let arr2 = [];

for (let i = 0; i < valuesArr.length; i++){
  if (    //could also just imput this below instead of index value
    valuesArr[i] !== valuesArr[0] && // "v1" <--
    valuesArr[i] !== valuesArr[2] && // "v3" <--
    valuesArr[i] !== valuesArr[4]    // "v5" <--
  ){
    arr2.push(valuesArr[i]);
  }
}

console.log(arr2);

Esto funciona. Sin embargo, haría una nueva matriz en el proceso. No estoy seguro de si eso es lo que desea o no, pero técnicamente sería una matriz que contiene solo los valores que desea.


fuente
-1

Puede probar y usar. delete array[index]Esto no eliminará completamente el elemento, sino que establece el valor en undefined.

Henesnarfel
fuente
Gracias pero quiero eliminar los elementos.
xyz
-1

Puede construir un a Setpartir de la matriz y luego crear una matriz a partir del conjunto.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Mohib
fuente