Rimuovi tutti gli elementi contenuti in un altro array


222

Sto cercando un modo efficiente per rimuovere tutti gli elementi da un array javascript se sono presenti in un altro array.

// If I have this array:
var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

// and this one:
var toRemove = ['b', 'c', 'g'];

Voglio operare su myArray per lasciarlo in questo stato: ['a', 'd', 'e', 'f']

Con jQuery, sto usando grep()e inArray(), che funziona bene:

myArray = $.grep(myArray, function(value) {
    return $.inArray(value, toRemove) < 0;
});

Esiste un modo javascript puro per farlo senza loop e splicing?




1
possibile duplicato di Puoi rimuovere un array da un altro in javascript o jquery - non puoi aver prestato molta attenzione ai suggerimenti fatti quando hai scritto la domanda
mplungjan

Non importa cosa, implicherà sempre il looping ad un certo livello.
Blue Skies,

Se vuoi davvero che sia "efficiente", non userai metodi di tipo funzionale come .filter(). Invece userete i forloop. Puoi evitare .splice()che l'ordine originale non debba essere mantenuto. Oppure ci sono modi per rendere .splice()più efficiente se pensi che ci saranno molti elementi da rimuovere.
Blue Skies,

Risposte:


379

Usa il Array.filter()metodo:

myArray = myArray.filter( function( el ) {
  return toRemove.indexOf( el ) < 0;
} );

Piccolo miglioramento, poiché il supporto del browser Array.includes()è aumentato:

myArray = myArray.filter( function( el ) {
  return !toRemove.includes( el );
} );

Prossimo adattamento usando le funzioni freccia :

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );

23
OP: Se stai usando Underscore.js c'è .difference()sostanzialmente quello che fa.
Bill Criswell,

Proprio quello che stavo cercando. Grazie. @ BillCriswell, controllerò il trattino basso.
Tocca il

1
@AlecRust Converte tutti gli elementi toRemove()in maiuscolo e cambia il callback da elin el.toUpperCase().
Sirko,

5
o più breve:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO

1
non è questo ordine n ^ 2?
Frazer Kirkman,

34

Il filtermetodo dovrebbe fare il trucco:

const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
const toRemove = ['b', 'c', 'g'];

// ES5 syntax
const filteredArray = myArray.filter(function(x) { 
  return toRemove.indexOf(x) < 0;
});

Se l' toRemovearray è di grandi dimensioni, questo tipo di modello di ricerca può essere inefficiente. Sarebbe più performante creare una mappa in modo che le ricerche siano O(1)piuttosto che O(n).

const toRemoveMap = toRemove.reduce(
  function(memo, item) {
    memo[item] = memo[item] || true;
    return memo;
  },
  {} // initialize an empty object
);

const filteredArray = myArray.filter(function (x) {
  return toRemoveMap[x];
});

// or, if you want to use ES6-style arrow syntax:
const toRemoveMap = toRemove.reduce((memo, item) => ({
  ...memo,
  [item]: true
}), {});

const filteredArray = myArray.filter(x => toRemoveMap[x]);

24

Se si utilizza una matrice di oggetti. Quindi il codice seguente dovrebbe fare la magia, in cui una proprietà dell'oggetto sarà il criterio per rimuovere gli oggetti duplicati.

Nell'esempio seguente, i duplicati sono stati rimossi confrontando il nome di ciascun elemento.

Prova questo esempio. http://jsfiddle.net/deepak7641/zLj133rh/

var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];

for( var i=myArray.length - 1; i>=0; i--){
 	for( var j=0; j<toRemove.length; j++){
 	    if(myArray[i] && (myArray[i].name === toRemove[j].name)){
    		myArray.splice(i, 1);
    	}
    }
}

alert(JSON.stringify(myArray));



11

I set ECMAScript 6 possono essere utilizzati per calcolare i diversi elementi di due array:

const myArray = new Set(['a', 'b', 'c', 'd', 'e', 'f', 'g']);
const toRemove = new Set(['b', 'c', 'g']);

const difference = new Set([...myArray].filter((x) => !toRemove.has(x)));

console.log(Array.from(difference)); // ["a", "d", "e", "f"]


8

Ho appena implementato come:

Array.prototype.exclude = function(list){
        return this.filter(function(el){return list.indexOf(el)<0;})
}

Usare come:

myArray.exclude(toRemove);

1
Non è buona pratica estendere prototypesoggetti nativi, ad esempio Array. Ciò può avere un conflitto a lungo termine con lo sviluppo futuro della lingua ( vedi il flattencaso )
MarcoL

6

Se non riesci a usare nuove cose ES5 come quelle, filterpenso che tu sia bloccato con due loop:

for( var i =myArray.length - 1; i>=0; i--){
  for( var j=0; j<toRemove.length; j++){
    if(myArray[i] === toRemove[j]){
      myArray.splice(i, 1);
    }
  }
}

il filtro non è "novità HTML5"
goofballLogic

Avrei dovuto scrivere "ES5 stuff". Non era disponibile con ES3
MarcoL l'

6
var myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
var toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];



myArray = myArray.filter(ar => !toRemove.find(rm => (rm.name === ar.name && ar.place === rm.place) ))

Ti dispiacerebbe aggiungere qualche spiegazione su una domanda così ben accolta, per favore?
harmonica141

1
Ho cercato ore per trovare una soluzione al problema e l'ho appena trovato, semplicemente fantastico. Grazie mille!
Tarvo Mäesepp,

5

Ora con un solo aroma:

console.log(['a', 'b', 'c', 'd', 'e', 'f', 'g'].filter(x => !~['b', 'c', 'g'].indexOf(x)))

Potrebbe non funzionare su vecchi browser.


3

Puoi usare _.differenceBy da lodash

const myArray = [
  {name: 'deepak', place: 'bangalore'}, 
  {name: 'chirag', place: 'bangalore'}, 
  {name: 'alok', place: 'berhampur'}, 
  {name: 'chandan', place: 'mumbai'}
];
const toRemove = [
  {name: 'deepak', place: 'bangalore'},
  {name: 'alok', place: 'berhampur'}
];
const sorted = _.differenceBy(myArray, toRemove, 'name');

Codice di esempio qui: CodePen


Cosa succede se l'attributo è nidificato all'interno dell'oggetto? Qualcosa di simile al test nel tuo caso {name: 'deepak', place: 'bangalore', nidificato: {test: 1}}
Charith Jayasanka

2

Che ne dici del più semplice possibile:

var myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
var toRemove = ['b', 'c', 'g'];

var myArray = myArray.filter((item) => !toRemove.includes(item));
console.log(myArray)


2
Tieni presente che includesnon è disponibile prima di ES7.
Greg,

0

Il modo corretto di rimuovere tutti gli elementi contenuti in un altro array è quello di rendere lo stesso oggetto source array rimuovendo solo gli elementi:

Array.prototype.removeContained = function(array) {
  var i, results;
  i = this.length;
  results = [];
  while (i--) {
    if (array.indexOf(this[i]) !== -1) {
      results.push(this.splice(i, 1));
    }
  }
  return results;
};

O equivalente di CoffeeScript:

Array.prototype.removeContained = (array) ->
  i = @length
  @splice i, 1 while i-- when array.indexOf(@[i]) isnt -1

Test all'interno degli strumenti di sviluppo di Chrome:

19: 33: 04.447 a = 1
19: 33: 06.354 b = 2
19: 33: 07.615 c = 3
19: 33: 09.981 arr = [a, b, c]
19: 33: 16.460 arr1 = arr

19: 33: 20.317 arr1 === arr
19: 33: 20.331 vero

19: 33: 43.592 arr.removeContained ([a, c])
19: 33: 52.433 arr === arr1
19: 33: 52.438 true

L'uso del framework angolare è il modo migliore per mantenere il puntatore all'oggetto di origine quando si aggiornano raccolte senza grandi quantità di watcher e ricariche.


Questa risposta è negativa in quanto annulla le migliori pratiche. In particolare, non modificare mai oggetti che non possiedi. In questo caso, stai modificando l'oggetto Array, che è un grande no-no.
Dev web ibrido,

0

Costruisco la logica senza utilizzare alcun metodo integrato, per favore fatemi sapere eventuali ottimizzazioni o modifiche. Ho testato nell'editor JS che funziona bene.

var myArray = [
            {name: 'deepak', place: 'bangalore'},
            {name: 'alok', place: 'berhampur'},
            {name: 'chirag', place: 'bangalore'},
            {name: 'chandan', place: 'mumbai'},

        ];
        var toRemove = [

            {name: 'chirag', place: 'bangalore'},
            {name: 'deepak', place: 'bangalore'},
            /*{name: 'chandan', place: 'mumbai'},*/
            /*{name: 'alok', place: 'berhampur'},*/


        ];
        var tempArr = [];
        for( var i=0 ; i < myArray.length; i++){
            for( var j=0; j<toRemove.length; j++){
                var toRemoveObj = toRemove[j];
                if(myArray[i] && (myArray[i].name === toRemove[j].name)) {
                    break;
                }else if(myArray[i] && (myArray[i].name !== toRemove[j].name)){
                        var fnd = isExists(tempArr,myArray[i]);
                        if(!fnd){
                            var idx = getIdex(toRemove,myArray[i])
                            if (idx === -1){
                                tempArr.push(myArray[i]);
                            }

                        }

                    }

                }
        }
        function isExists(source,item){
            var isFound = false;
            for( var i=0 ; i < source.length; i++){
                var obj = source[i];
                if(item && obj && obj.name === item.name){
                    isFound = true;
                    break;
                }
            }
            return isFound;
        }
        function getIdex(toRemove,item){
            var idex = -1;
            for( var i=0 ; i < toRemove.length; i++){
                var rObj =toRemove[i];
                if(rObj && item && rObj.name === item.name){
                    idex=i;
                    break;
                }
            }
            return idex;
        }
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.