Supprimer tous les éléments contenus dans un autre tableau

222

Je recherche un moyen efficace de supprimer tous les éléments d'un tableau javascript s'ils sont présents dans un autre tableau.

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

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

Je souhaite opérer sur myArray pour le laisser dans cet état: ['a', 'd', 'e', 'f']

Avec jQuery, j'utilise grep()et inArray(), ce qui fonctionne bien:

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

Existe-t-il un moyen pur javascript de le faire sans boucle ni épissage?

Robinet
la source
1
doublon possible de Supprimer l'élément spécifique d'un tableau?
mplungjan
4
double possible différence de tableau JavaScript
Pills Explosion
1
doublon possible de Pouvez-vous supprimer un tableau d'un autre en javascript ou jquery - vous ne pouvez pas avoir prêté beaucoup d'attention aux suggestions faites lorsque vous avez écrit la question
mplungjan
Quoi qu'il en soit, cela impliquera toujours une boucle à un certain niveau.
Blue Skies
Si vous voulez vraiment qu'il soit "efficace", vous n'utiliserez pas de méthodes de type fonctionnel comme .filter(). À la place, vous utiliserez des forboucles. Vous pouvez éviter .splice()si la commande d'origine n'a pas besoin d'être maintenue. Ou il existe des moyens de rendre .splice()plus efficace si vous pensez qu'il y aura de nombreux éléments à supprimer.
Blue Skies

Réponses:

379

Utilisez la Array.filter()méthode:

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

Petite amélioration, car la prise en charge du navigateur pour Array.includes()a augmenté:

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

Prochaine adaptation à l'aide des fonctions flèches :

myArray = myArray.filter( ( el ) => !toRemove.includes( el ) );
Sirko
la source
23
OP: Si vous utilisez Underscore.js, il y a .difference()ce qui fait essentiellement cela.
Bill Criswell
Exactement ce que je cherchais. Je vous remercie. @BillCriswell, je vais vérifier le soulignement.
Appuyez sur
1
@AlecRust Convertissez tous les éléments de toRemove()en majuscules et modifiez le rappel de elà el.toUpperCase().
Sirko
5
ou moins:myArray = myArray.filter( el => !toRemove.includes( el ) );
538ROMEO
1
n'est-ce pas l'ordre n ^ 2?
Frazer Kirkman
34

La filterméthode devrait faire l'affaire:

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

Si votre toRemovetableau est grand, ce type de modèle de recherche peut être inefficace. Il serait plus performant de créer une carte pour que les recherches soient O(1)plutôt que 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
la source
24

Si vous utilisez un tableau d'objets. Ensuite, le code ci-dessous devrait faire la magie, où une propriété d'objet sera le critère pour supprimer les éléments en double.

Dans l'exemple ci-dessous, les doublons ont été supprimés en comparant le nom de chaque élément.

Essayez cet exemple. 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
la source
11

Les ensembles ECMAScript 6 peuvent être utilisés pour calculer les différents éléments de deux tableaux:

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
la source
8

Je viens de mettre en œuvre en tant que:

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

Utilisé comme:

myArray.exclude(toRemove);
user2582833
la source
1
Il n'est pas recommandé d'étendre prototypesdes objets natifs, tels Array. Cela peut avoir un conflit à long terme avec le développement futur de la langue ( voir le flattencas )
MarcoL
6

Si vous ne pouvez pas utiliser de nouvelles choses ES5, filterje pense que vous êtes coincé avec deux boucles:

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
la source
le filtre n'est pas un "nouveau truc HTML5"
goofballLogic
J'aurais dû écrire "ES5 stuff". Il n'était pas disponible avec 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
la source
Pourriez-vous ajouter quelques explications sur une question aussi bien reçue, s'il vous plaît?
harmonica141
1
J'ai cherché des heures pour trouver une solution au problème et je l'ai trouvé, tout simplement génial. Merci beaucoup!
Tarvo Mäesepp
5

Maintenant dans la saveur d'une doublure:

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

Peut ne pas fonctionner sur les anciens navigateurs.

Matas Vaitkevicius
la source
3

Vous pouvez utiliser _.differenceBy depuis 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');

Exemple de code ici: CodePen

Craciun Ciprian
la source
Que faire si l'attribut est imbriqué dans l'objet? Quelque chose comme dans votre cas {nom: 'deepak', lieu: 'bangalore', imbriqué: {test: 1}}
Charith Jayasanka
2

Que diriez-vous du plus simple possible:

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
la source
2
Gardez à l'esprit que ce includesn'est pas disponible avant ES7.
Greg
0

La bonne façon de supprimer tous les éléments contenus dans un autre tableau consiste à faire du tableau source le même objet en supprimant uniquement les éléments:

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

Ou équivalent CoffeeScript:

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

Test à l'intérieur des outils de développement 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 vrai

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

L'utilisation du cadre angulaire est le meilleur moyen de conserver le pointeur sur l'objet source lorsque vous mettez à jour des collections sans une grande quantité d'observateurs et de rechargements.

Михаил Юдин
la source
Cette réponse est mauvaise car elle annule les meilleures pratiques. Plus précisément, ne modifiez jamais des objets que vous ne possédez pas. Dans ce cas, vous modifiez l'objet Array, ce qui est un gros non-non.
Développeur web hybride du
0

Je construis la logique sans utiliser de méthodes intégrées, veuillez me faire part de toute optimisation ou modification. J'ai testé dans l'éditeur JS, cela fonctionne bien.

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
la source