Rimuovi più elementi dall'array in Javascript / jQuery


117

Ho due array. Il primo array contiene alcuni valori mentre il secondo array contiene gli indici dei valori che dovrebbero essere rimossi dal primo array. Per esempio:

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

Voglio rimuovere i valori presenti negli indici 0,2,4da valuesArr. Ho pensato che il splicemetodo nativo potesse aiutare, quindi ho pensato :

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

Ma non ha funzionato perché dopo ciascuno splice, gli indici dei valori valuesArrerano diversi. Potrei risolvere questo problema utilizzando un array temporaneo e copiando tutti i valori nel secondo array, ma mi chiedevo se ci sono metodi nativi a cui possiamo passare più indici in cui rimuovere i valori da un array.

Preferirei una soluzione jQuery. (Non sono sicuro di poterlo usare grepqui)

Risposte:


256

C'è sempre il semplice vecchio forloop:

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

Procedi removeValFromIndexin ordine inverso e puoi .splice()senza rovinare gli indici degli elementi ancora da rimuovere.

Nota in quanto sopra ho usato la sintassi del letterale array con parentesi quadre per dichiarare i due array. Questa è la sintassi consigliata perché l' new Array()uso è potenzialmente fonte di confusione dato che risponde in modo diverso a seconda di quanti parametri si passano.

EDIT : Ho appena visto il tuo commento su un'altra risposta sull'array di indici non necessariamente in un ordine particolare. In questo caso, ordinalo in ordine decrescente prima di iniziare:

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

E seguilo con qualsiasi $.each()metodo di loop / / ecc.


1
non affettate rovinare l'indice
Muhammad Umer

5
@ MuhammadUmer - No, non se lo fai correttamente, che è ciò che spiega la mia risposta.
nnnnnn

5
Grazie per la consapevolezza dell'ordine inverso.
Daniel Nalbach

2
+1, non mi sono reso conto che dovevo fare la giunzione in ordine inverso, anche se invece di usare forEach, il mio approccio sta usando$.each(rvm.reverse(), function(e, i ) {})
Luis Stanley Jovel

1
questo funzionerà solo se removeValFromIndex è ordinato in ordine crescente
Kunal Burangi

24

Eccone uno che uso quando non vado con lodash / underscore:

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

Soluzione elegante! All'inizio ho pensato che non avrebbe funzionato perché pensavo che ogni volta che chiami sliceavresti dovuto ricalcolare gli indici da rimuovere (-1 in poi IndexestoBeRemoved), ma in realtà funziona!
Renato Gama

2
Troppo intelligente
Farzad YZ

11
Questa soluzione funziona se solo l' IndexesToBeRemovedarray è ordinato in ordine crescente.
xfg

Tali indici verranno invalidati dopo la prima giunzione.
shinzou

@shinzou - Non se IndexesToBeRemovedè ordinato (crescente).
nnnnnn

18

Non in-placema può essere fatto utilizzando grepe le inArrayfunzioni di jQuery.

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

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

controlla questo violino.


Sarebbe (abbastanza vicino a) sul posto se valuesArr = $.grep(...);
dicessi

1
@nnnnnn ah ah ah stavo partendo per un incontro (sai che ti rendono così produttivo) quindi non ho sperimentato molto.
TheVillageIdiot

1
@ cept0 Perché il voto negativo? OP ha chiesto una soluzione jQuery.
Ste77

Grazie mille! @TheVillageIdiot
ecorvo

jsfiddler - Errore 404. Siamo veramente spiacenti, ma questa pagina non esiste.
Ash


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

Il riferimento MDN è qui


@ riship89 Il violino è abbassato
mate64

@Karna: fiddle is down
riship89

1
Si prega di notare che al momento della scrittura (giugno 2014), Array.prototype.find fa parte dell'attuale bozza ES6 ed è implementato solo nell'attuale firefox
Olli K

6

In JS puro puoi scorrere l'array all'indietro, in modo splice()da non rovinare gli indici degli elementi successivi nel ciclo:

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

Non funziona che schifo non è una funzione, quindi si presume che sia l'indice degli indici di elementi indesiderati e si usa schifo [arr [i]]
DavChana

5

È necessario pubblicare una risposta con il O(n)tempo :). Il problema con la soluzione di giunzione è che a causa dell'implementazione sottostante dell'array che è letteralmente un array , ogni splicechiamata richiederà O(n)tempo. Questo è più pronunciato quando impostiamo un esempio per sfruttare questo 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)

Questo rimuove gli elementi a partire dal centro all'inizio, quindi ogni rimozione forza il motore js a copiare n/2elementi, abbiamo (n/2)^2operazioni di copia in totale che sono quadratiche.

La soluzione di giunzione (supponendo che issia già ordinata in ordine decrescente per eliminare le spese generali) funziona in questo modo:

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

Tuttavia, non è difficile implementare una soluzione temporale lineare, ricostruendo l'array da zero, utilizzando una maschera per vedere se copiamo elementi o meno (l'ordinamento lo spingerà a O(n)log(n)). La seguente è un'implementazione di questo tipo (non che maskè booleano invertito per la velocità):

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

L'ho eseguito su jsperf.com e anche n=100il metodo di giunzione è del 90% più lento. Per maggiori nquesta differenza sarà molto maggiore.


5

Quick ES6 una fodera:

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

Il tuo codice dovrebbe essere eseguito più velocemente se crei removeValFromIndexun Set()e usi removeValFromIndex.hasinvece di includes.
Boris

5

Una soluzione semplice ed efficiente (complessità lineare) che utilizza filtri e 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);

Il grande vantaggio di questa implementazione è che l'operazione ( hasfunzione) Set lookup richiede un tempo costante, essendo più veloce della risposta di nevace, per esempio.


@MichaelPaccione Glad to help :)
Alberto Trindade Tavares

3

Questo funziona bene per me e funziona anche quando elimino da un array di oggetti:

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

Potrebbe esserci un modo più breve e più efficiente per scrivere questo, ma funziona.


2

Una soluzione semplice utilizzando ES5. Questo sembra più appropriato per la maggior parte delle applicazioni al giorno d'oggi, dal momento che molti non vogliono più fare affidamento su jQuery ecc.

Quando gli indici da rimuovere sono ordinati in ordine crescente:

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

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

Quando gli indici da rimuovere non sono ordinati:

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

Puoi correggere il tuo codice sostituendolo removeValFromIndexcon removeValFromIndex.reverse(). Se non è garantito che quell'array utilizzi l'ordine crescente, puoi invece usare removeValFromIndex.sort(function(a, b) { return b - a }).


Mi sembra buono - jsfiddle.net/mrtsherman/gDcFu/2 . Anche se questo fa supporre che l'elenco di rimozione sia in ordine.
mrtsherman

@minopret: Grazie ma funzionerà solo se gli indici in removeValFromIndexsono in ordine crescente.
xyz


1

Se stai usando underscore.js , puoi usare _.filter()per risolvere il tuo 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);
                });

Inoltre, se stai tentando di rimuovere elementi utilizzando un elenco di elementi anziché gli indici, puoi semplicemente utilizzare _.without(), in questo modo:

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

Ora filteredArrdovrebbe essere["V2", "V4", "V5"]


How contains è implementato in underscore ... fai attenzione se è equivalente a indexOf all'interno del filtro ... non è affatto ottimale ...
Alvaro Joao

1

filtro + indexOf (IE9 +):

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

O con filtro ES6 + trova (Edge +):

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

indexDi dentro il filtro ... non ottimale
Alvaro Joao

1

Ecco una sveltina.

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]

indexDell'interno del filtro ... non ottimale
Alvaro Joao

0

Sembra che Applica potrebbe essere quello che stai cercando.
forse qualcosa del genere funzionerebbe?

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

Ma il .splice()metodo non si aspetta un elenco di elementi da rimuovere, si aspetta un singolo indice dell'elemento da cui iniziare la rimozione seguito dal numero di elementi da rimuovere ...
nnnnnn

0

Per più articoli o un articolo unico:

Ti suggerisco di usare Array.prototype.filter

Non usare mai indexOf se conosci già index !:

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

Fare:

con hash ... utilizzando 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);

Funziona. Tuttavia, nel processo creeresti un nuovo array. Non sono sicuro se questo lo desideri o meno, ma tecnicamente sarebbe un array contenente solo i valori che desideri.


-1

Potresti provare a usare delete array[index]Questo non rimuoverà completamente l'elemento ma piuttosto imposta il valore su undefined.


Grazie ma voglio rimuovere gli elementi.
xyz

-1

È possibile costruire un Setarray dall'array e quindi creare un array dall'insieme.

const array = [1, 1, 2, 3, 5, 5, 1];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // Result: [1, 2, 3, 5]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.