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?



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

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

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



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


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

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

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

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.


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


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)


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.


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;
        }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.