Supprimer des éléments d'une liste dans une autre

206

J'essaie de comprendre comment parcourir une liste générique d'éléments que je souhaite supprimer d'une autre liste d'éléments.

Alors disons que j'ai ceci comme exemple hypothétique

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();

Je veux parcourir list1 avec un foreach et supprimer chaque élément de List1 qui est également contenu dans List2.

Je ne sais pas trop comment procéder, car foreach n'est pas basé sur un index.

PositiveGuy
la source
1
Vous souhaitez supprimer des éléments de List1 qui se trouvent également dans List2?
Srinivas Reddy Thatiparthy
1
Que doit-il se passer si vous avez list1 = {foo1} et list2 = {foo1, foo1}. Toutes les copies de foo1 doivent-elles être supprimées de list2, ou juste la première?
Mark Byers
2
-1 - J'ai dévalorisé toutes les réponses à cette question parce que je pensais qu'elles étaient toutes fausses, mais il semble que la question soit simplement horrible. Maintenant, je ne peux pas les changer - excuses. Voulez-vous supprimer les éléments list1qui existent dans list2ou voulez-vous supprimer les éléments list2qui existent dans list1? Au moment de ce commentaire, chaque réponse fournie effectuera cette dernière.
John Rasch
7
@John Rashch, vous devriez être un peu moins heureux de déclencher ces downvotes. Certaines réponses sont assez conceptuelles et ne montrent que comment réaliser ce que le PO souhaite sans même se rapporter aux listes mentionnées dans la question.
João Angelo
3
@Mark - vous avez raison, ma faute entièrement - c'est pourquoi j'ai mis le commentaire ici en expliquant ce qui s'est passé, je cherchais une réponse précédente que j'avais déjà eue à une question similaire entre-temps après mon vote et j'allais partir commentaires après l'avoir trouvé - il s'avère que ce n'est pas le meilleur processus pour cela!
John Rasch

Réponses:

358

Vous pouvez utiliser Except :

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();
List<car> result = list2.Except(list1).ToList();

Vous n'avez probablement même pas besoin de ces variables temporaires:

List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();

Notez que Exceptcela ne modifie aucune des listes - il crée une nouvelle liste avec le résultat.

Mark Byers
la source
13
Point mineur, mais cela produira un IEnumerable<car>, pas un List<car>. Vous devez appeler ToList()pour obtenir une liste. De plus, je pense que ça devrait êtreGetSomeOtherList().Except(GetTheList()).ToList()
Adam Robinson
9
Vous en aurez également besoin using System.Linq;si vous ne l'aviez pas auparavant.
yellavon
1
Remarque: list1.Except (list2) ne donnera pas le même résultat que list2.Except (list1). Le dernier a fonctionné pour moi.
radbyx
2
Soyez prudent lors de l'utilisation Exceptcar cela effectue en fait une opération de définition , qui distingue la liste résultante. Je ne m'attendais pas à ce comportement car j'utilise un List, pas un HashSet. En relation.
logan
4
Comment se fait-il que ce soit la bonne réponse? Bien sûr, cela pourrait vous donner ce que vous voulez dans votre contexte, cependant, "Supprimer des éléments d'une liste dans une autre" n'est certainement pas équivalent à une opération de différence définie, et vous ne devriez pas désinformer les gens en acceptant cela comme la bonne réponse !!!!
user1935724
37

Vous n'avez pas besoin d'un index, car la List<T>classe vous permet de supprimer des éléments par valeur plutôt que par index en utilisant la Removefonction.

foreach(car item in list1) list2.Remove(item);
Adam Robinson
la source
3
+1, mais à l'OMI, vous devez utiliser des crochets autour de la list2.Remove(item);déclaration.
ANeves
2
@sr pt: j'utilise toujours des crochets sur les instructions qui apparaissent sur une autre ligne, mais pas sur les blocs à instruction unique que je peux / fais placer sur la même ligne que l'instruction de contrôle de flux.
Adam Robinson
4
@uriz: Sans tenir compte des qualifications de ce qui serait élégant, c'est la seule réponse qui fait réellement ce que dit la question (supprime les éléments de la liste principale); l'autre réponse crée une nouvelle liste, ce qui peut ne pas être souhaitable si la liste est transmise par un appelant différent qui s'attend à ce qu'elle soit modifiée au lieu d'obtenir une liste de remplacement.
Adam Robinson
5
@uriz @AdamRobinson puisque nous discutons de solutions élégantes ...list1.ForEach(c => list2.Remove(c));
David Sherret
1
"élégant" devrait signifier que "le développeur qui maintient ce code le trouvera simple et facile à comprendre", c'est pourquoi c'est la meilleure réponse.
Seth
22

Je recommanderais d'utiliser les méthodes d'extension LINQ . Vous pouvez facilement le faire avec une seule ligne de code comme ceci:

list2 = list2.Except(list1).ToList();

Cela suppose bien sûr que les objets de list1 que vous supprimez de list2 sont la même instance.

Berkshire
la source
2
Il supprime également les doublons.
JuilletOrdinaire du
17

Dans mon cas, j'avais deux listes différentes, avec un identifiant commun, un peu comme une clé étrangère. La deuxième solution citée par "nzrytmn" :

var result =  list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();

C'était celui qui convenait le mieux à ma situation. J'avais besoin de charger une DropDownList sans les enregistrements qui avaient déjà été enregistrés.

Je vous remercie !!!

Voici mon code:

t1 = new T1();
t2 = new T2();

List<T1> list1 = t1.getList();
List<T2> list2 = t2.getList();

ddlT3.DataSource= list2.Where(s => !list1.Any(p => p.Id == s.ID)).ToList();
ddlT3.DataTextField = "AnyThing";
ddlT3.DataValueField = "IdAnyThing";
ddlT3.DataBind();
Gabriel Santos Reis
la source
vous n'avez jamais expliqué ce qu'était DDlT3
rogue39nin
15

Vous pourriez utiliser LINQ, mais j'irais avec la RemoveAllméthode. Je pense que c'est celui qui exprime le mieux votre intention.

var integers = new List<int> { 1, 2, 3, 4, 5 };

var remove = new List<int> { 1, 3, 5 };

integers.RemoveAll(i => remove.Contains(i));
João Angelo
la source
9
Ou encore plus simple avec les groupes de méthodes que vous pouvez faire - integers.RemoveAll (remove.Contains);
Ryan
12
list1.RemoveAll(l => list2.Contains(l));
Alexandre Amado de Castro
la source
alias "totalement impur" :-)
Xan-Kun Clark-Davis
Quel mal avec ça. Il semble préférable de créer une autre liste à l'aide d'Except. Surtout lorsque les deux listes sont très petites.
Mike Keskinov
1
Étant donné que les deux méthodes de liste le sont O(N), cela entraînera O(N^2)un problème avec les grandes listes.
tigrou
7

Solution 1: Vous pouvez faire comme ceci:

List<car> result = GetSomeOtherList().Except(GetTheList()).ToList();

Mais dans certains cas, cette solution peut ne pas fonctionner. si cela ne fonctionne pas, vous pouvez utiliser ma deuxième solution.

Solution 2:

List<car> list1 = GetTheList();
List<car> list2 = GetSomeOtherList();

nous prétendons que list1 est votre liste principale et list2 est votre liste secondaire et que vous voulez obtenir des éléments de list1 sans éléments de list2.

 var result =  list1.Where(p => !list2.Any(x => x.ID == p.ID && x.property1 == p.property1)).ToList();
nzrytmn
la source
0

Comme Exceptcela ne modifie pas la liste, vous pouvez utiliser ForEach sur List<T>:

list2.ForEach(item => list1.Remove(item));

Ce n'est peut-être pas le moyen le plus efficace, mais il est simple, donc lisible, et il met à jour la liste d'origine (ce qui est mon exigence).

Necriis
la source
-3

Ça y est ..

    List<string> list = new List<string>() { "1", "2", "3" };
    List<string> remove = new List<string>() { "2" };

    list.ForEach(s =>
        {
            if (remove.Contains(s))
            {
                list.Remove(s);
            }
        });
Ian P
la source
3
-1. Cela lèvera une exception après la suppression du premier élément. En outre, c'est (généralement) une meilleure idée de parcourir la liste à supprimer , car elle est généralement plus petite. Vous forcez également davantage de parcours de liste à le faire de cette façon.
Adam Robinson