Entfernen Sie alle in einem anderen Array enthaltenen Elemente

222

Ich suche nach einer effizienten Möglichkeit, alle Elemente aus einem Javascript-Array zu entfernen, wenn sie in einem anderen Array vorhanden sind.

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

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

Ich möchte myArray bearbeiten, um es in diesem Zustand zu belassen: ['a', 'd', 'e', 'f']

Mit jQuery verwende ich grep()und inArray(), was gut funktioniert:

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

Gibt es eine reine Javascript-Möglichkeit, dies ohne Schleifen und Spleißen zu tun?

Zapfhahn
quelle
1
Mögliches Duplikat von Bestimmtes Element aus einem Array entfernen?
Mplungjan
4
Mögliches Duplikat des JavaScript-Array-Unterschieds
Explosion Pills
1
Mögliches Duplikat von Können Sie ein Array in Javascript oder jquery aus einem anderen entfernen? Sie können den Vorschlägen, die Sie beim Schreiben der Frage gemacht haben, nicht viel Aufmerksamkeit geschenkt haben
mplungjan
Egal was passiert, es wird immer eine Schleife auf einer bestimmten Ebene beinhalten.
Blue Skies
Wenn Sie wirklich wollen, dass es "effizient" ist, werden Sie keine funktionalen Typmethoden wie verwenden .filter(). Stattdessen verwenden Sie forSchleifen. Sie können vermeiden, .splice()dass die ursprüngliche Bestellung nicht gepflegt werden muss. Oder es gibt Möglichkeiten, .splice()effizienter zu arbeiten, wenn Sie glauben, dass viele Elemente entfernt werden müssen.
Blue Skies

Antworten:

379

Verwenden Sie die Array.filter()Methode:

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

Kleine Verbesserung, da die Browserunterstützung für Array.includes()zugenommen hat:

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

Nächste Anpassung mit Pfeilfunktionen :

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );
Sirko
quelle
23
OP: Wenn Sie Underscore.js verwenden, gibt es das, .difference()was dies im Grunde tut.
Bill Criswell
Genau das, wonach ich gesucht habe. Danke dir. @ BillCriswell, ich werde den Unterstrich überprüfen.
Tippen Sie auf den
1
@AlecRust Konvertiert alle Elemente toRemove()in Großbuchstaben und ändert den Rückruf von elnach el.toUpperCase().
Sirko
5
oder kürzer:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO
1
ist diese Reihenfolge nicht n ^ 2?
Frazer Kirkman
34

Die filterMethode sollte den Trick machen:

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

Wenn Ihr toRemoveArray groß ist, kann diese Art von Suchmuster ineffizient sein. Es wäre performanter, eine Karte so zu erstellen, dass Lookups O(1)eher als sind 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]);
Ashwin Balamohan
quelle
24

Wenn Sie ein Array von Objekten verwenden. Dann sollte der folgende Code die Magie ausführen, wobei eine Objekteigenschaft das Kriterium zum Entfernen doppelter Elemente ist.

Im folgenden Beispiel wurden Duplikate entfernt, um den Namen jedes Elements zu vergleichen.

Versuchen Sie dieses Beispiel. 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));

Deepak Acharya
quelle
11

ECMAScript 6-Sätze können zur Berechnung der verschiedenen Elemente von zwei Arrays verwendet werden:

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"]

Benny Neugebauer
quelle
8

Ich habe gerade implementiert als:

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

Benutzen als:

myArray.exclude(toRemove);
user2582833
quelle
1
Es ist keine gute Praxis, prototypesnative Objekte zu erweitern, wie z Array. Das kann einen langfristigen Konflikt mit der zukünftigen Entwicklung der Sprache haben ( siehe flattenFall )
MarcoL
6

Wenn Sie kein neues ES5-Material verwenden können, filterstecken Sie wahrscheinlich in zwei Schleifen:

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);
    }
  }
}
MarcoL
quelle
Filter ist nicht "neues HTML5-Zeug"
goofballLogic
Ich hätte "ES5-Zeug" schreiben sollen. Es war nicht verfügbar mit ES3
MarcoL
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) ))
Mojtaba Roohi
quelle
Würde es Ihnen etwas ausmachen, eine Erklärung zu einer so gut aufgenommenen Frage hinzuzufügen?
Mundharmonika141
1
Ich suchte stundenlang nach einer Lösung für das Problem und fand es einfach großartig. Vielen Dank!
Tarvo Mäesepp
5

Jetzt im Einzeiler-Geschmack:

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

Funktioniert möglicherweise nicht mit alten Browsern.

Matas Vaitkevicius
quelle
3

Sie können _.differenceBy von lodash verwenden

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

Beispielcode hier: CodePen

Craciun Ciprian
quelle
Was ist, wenn das Attribut im Objekt verschachtelt ist? Etwas Test wie in Ihrem Fall {Name: 'Deepak', Ort: 'Bangalore', verschachtelt: {Test: 1}}
Charith Jayasanka
2

Wie wäre es mit dem einfachsten:

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)

Eggon
quelle
2
includesBeachten Sie, dass dies vor ES7 nicht verfügbar ist.
Greg
0

Der richtige Weg, um alle in einem anderen Array enthaltenen Elemente zu entfernen, besteht darin, das Quellarray zum gleichen Objekt zu machen, indem nur Elemente entfernt werden:

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

Oder CoffeeScript-Äquivalent:

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

Testen in Chrome Dev Tools:

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 wahr

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

Die Verwendung des Angular-Frameworks ist der beste Weg, um den Zeiger auf das Quellobjekt zu behalten, wenn Sie Sammlungen ohne große Anzahl von Beobachtern aktualisieren und neu laden.

Михаил Юдин
quelle
Diese Antwort ist schlecht, da sie Best Practices ungültig macht. Ändern Sie niemals Objekte, die Sie nicht besitzen. In diesem Fall ändern Sie das Array-Objekt, das ein großes Nein-Nein ist.
Hybrid Web Dev
0

Ich erstelle die Logik ohne integrierte Methoden. Bitte teilen Sie mir Optimierungen oder Änderungen mit. Ich habe im JS-Editor getestet, dass es gut funktioniert.

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;
        }
Shravan R.
quelle