Remova vários elementos da matriz em Javascript / jQuery


117

Eu tenho duas matrizes. O primeiro array contém alguns valores, enquanto o segundo array contém índices dos valores que devem ser removidos do primeiro array. Por exemplo:

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

Quero remover os valores presentes nos índices 0,2,4de valuesArr. Achei que o splicemétodo nativo pudesse ajudar, então pensei em:

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

Mas não funcionou porque depois de cada um splice, os índices dos valores em valuesArreram diferentes. Eu poderia resolver esse problema usando um array temporário e copiando todos os valores para o segundo array, mas gostaria de saber se há algum método nativo para o qual possamos passar vários índices para remover valores de um array.

Eu preferiria uma solução jQuery. (Não tenho certeza se posso usar grepaqui)

Respostas:


256

Sempre há o velho e simples forciclo:

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

Percorra removeValFromIndexna ordem inversa e você pode .splice()sem bagunçar os índices dos itens que ainda serão removidos.

Observe acima que usei a sintaxe literal de matriz com colchetes para declarar as duas matrizes. Esta é a sintaxe recomendada porque o new Array()uso é potencialmente confuso, visto que responde de maneira diferente dependendo de quantos parâmetros você passa.

EDIT : Acabei de ver o seu comentário em outra resposta sobre a matriz de índices não estar necessariamente em uma ordem particular. Se for esse o caso, basta classificá-lo em ordem decrescente antes de começar:

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

E siga isso com qualquer $.each()método de loop / / etc. que desejar.


1
não vai bagunçar o índice
Muhammad Umer

5
@MuhammadUmer - Não, se você fizer corretamente, que é o que minha resposta explica.
nnnnnn

5
Obrigado pela consciência de ordem inversa.
Daniel Nalbach

2
+1, eu não percebi que tinha que fazer a emenda na ordem inversa, embora em vez de usar forEach, minha abordagem está usando$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel

1
isso só funcionará se removeValFromIndex estiver classificado em ordem crescente
Kunal Burangi

24

Aqui está um que eu uso quando não vou com lodash / sublinhado:

while(IndexesToBeRemoved.length) {
    elements.splice(IndexesToBeRemoved.pop(), 1);
}

Solução elegante! A princípio pensei que não funcionaria porque pensei que toda vez que você ligasse sliceteria que recalcular os índices a serem removidos (-1 em IndexestoBeRemoved), mas realmente funciona!
Renato Gama

2
Muito inteligente
Farzad YZ

11
Esta solução funciona se apenas o IndexesToBeRemovedarray for classificado em ordem crescente.
xfg

Esses índices serão invalidados após a primeira emenda.
shinzou

@shinzou - Não se IndexesToBeRemovedfor classificado (crescente).
nnnnnn

18

Não in-place, mas pode ser feito usando grepe inArrayfunções de jQuery.

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

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

verifique este violino.


Estaria (próximo o suficiente) no lugar se você dissessevaluesArr = $.grep(...);
nnnnnn

1
@nnnnnn ha ha ha Eu estava saindo para uma reunião (você sabe que eles o tornam tão produtivo), então não experimentei muito.
TheVillageIdiot

1
@ cept0 Por que o voto negativo? OP pediu uma solução jQuery.
Ste77

Muito obrigado! @TheVillageIdiot
ecorvo

jsfiddler - Erro 404. Lamentamos muito, mas essa página não existe.
Ash


7
function filtermethod(element, index, array) {  
    return removeValFromIndex.find(index)
}  
var result = valuesArr.filter(filtermethod);

A referência MDN está aqui


@ riship89 O violino caiu
mate64

@Karna: o violino caiu
riship89

1
Por favor, note que no momento da escrita (junho de 2014), Array.prototype.find é parte do rascunho ES6 atual e apenas implementado no firefox
Olli K

6

Em JS puro, você pode percorrer a matriz de trás para frente, portanto splice(), não bagunçará os índices dos elementos a seguir no loop:

for (var i = arr.length - 1; i >= 0; i--) {
    if ( yuck(arr[i]) ) {
        arr.splice(i, 1);
    }
}

Não funciona, eca não é uma função, então presume-se que seja o índice de índices de elementos indesejados, e eca usada [arr [i]]
DavChana

5

Parece necessário postar uma resposta com o O(n)tempo :). O problema com a solução de emenda é que, devido à implementação subjacente do array ser literalmente um array , cada splicechamada levará O(n)algum tempo. Isso é mais evidente quando configuramos um exemplo para explorar este comportamento:

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)

Isso remove elementos do meio para o início, portanto, cada remoção força o mecanismo js a copiar os n/2elementos, temos (n/2)^2operações de cópia no total, que são quadráticas.

A solução de emenda (assumindo que isjá está classificada em ordem decrescente para se livrar dos overheads) é assim:

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

No entanto, não é difícil implementar uma solução de tempo linear, reconstruindo o array do zero, usando uma máscara para ver se copiamos os elementos ou não (sort irá empurrar para O(n)log(n)). O seguinte é tal implementação (não que maskseja booleano invertido para velocidade):

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

Eu executei isso em jsperf.com e até mesmo n=100o método de emenda é 90% mais lento. Para maiores nessa diferença será muito maior.


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

Seu código deve ser executado mais rápido se você fizer removeValFromIndexum Set()e usar em removeValFromIndex.hasvez de includes.
Boris

5

Uma solução simples e eficiente (complexidade linear) usando filtro e Conjunto :

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

A grande vantagem dessa implementação é que a operação Set lookup ( hasfunção) leva um tempo constante, sendo mais rápida do que a resposta de Nevace, por exemplo.


@MichaelPaccione Fico feliz em ajudar :)
Alberto Trindade Tavares

3

Isso funciona bem para mim e também ao excluir de uma série 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));

Pode haver uma maneira mais curta e mais eficiente de escrever isso, mas funciona.


2

Uma solução simples usando ES5. Isso parece mais apropriado para a maioria dos aplicativos hoje em dia, já que muitos não querem mais depender do jQuery etc.

Quando os índices a serem removidos são classificados em ordem crescente:

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

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

Quando os índices a serem removidos não são classificados:

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

1

Você pode corrigir seu código substituindo removeValFromIndexpor removeValFromIndex.reverse(). Se não for garantido que essa matriz use a ordem crescente, você pode usar removeValFromIndex.sort(function(a, b) { return b - a }).


Parece bom para mim - jsfiddle.net/mrtsherman/gDcFu/2 . Embora isso faça a suposição de que a lista de remoção está em ordem.
mrtsherman

@minopret: Obrigado, mas só funcionará se os índices removeValFromIndexestiverem em ordem crescente.
xyz


1

Se você estiver usando o underscore.js , você pode usar _.filter()para resolver o seu 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);
                });

Além disso, se você estiver tentando remover itens usando uma lista de itens em vez de índices, você pode simplesmente usar _.without(), assim:

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

Agora filteredArrdeveria ser["V2", "V4", "V5"]


Como o contains é implementado em sublinhado ... tome cuidado se é equivalente a indexOf dentro do filtro ... não é o ideal de forma alguma ...
Alvaro Joao

1

filtro + indexOf (IE9 +):

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

Ou com filtro ES6 + encontrar (Edge +):

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

indexOf dentro do filtro ... não ótimo
Alvaro João

1

Aqui está uma rapidinha.

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]

indexOf dentro do filtro ... não ótimo
Alvaro João

0

Parece que o Apply pode ser o que você está procurando.
talvez algo assim funcionasse?

Array.prototype.splice.apply(valuesArray, removeValFromIndexes );

Mas o .splice()método não espera uma lista de elementos para remover, ele espera um único índice do elemento no qual iniciar a remoção, seguido pelo número de elementos a serem removidos ...
nnnnnn

0

Para vários itens ou item único:

Eu sugiro que você use Array.prototype.filter

Nunca use indexOf se você já conhece o í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

Faz:

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

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

Isso funciona. No entanto, você faria uma nova matriz no processo. Não tenho certeza se isso você deseja ou não, mas tecnicamente seria uma matriz contendo apenas os valores que você deseja.


-1

Você pode tentar e usar delete array[index]Isso não removerá completamente o elemento, mas definirá o valor como undefined.


Obrigado, mas quero remover os elementos.
xyz

-1

Você pode construir um a Setpartir do array e então criar um array a partir do conjunto.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.