251
votes

I'm trying to figure out how to traverse a generic list of items that I want to remove from another list of items.

So let's say I have this as a hypothetical example

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

I want to traverse list1 with a foreach and remove each item in List1 which is also contained in List2.

I'm not quite sure how to go about that as foreach is not index based.

9
You want to remove items in List1 which are also in List2? - Srinivas Reddy Thatiparthy
What should happen if you have list1 = { foo1 } and list2 = { foo1, foo1 }. Should all copies of foo1 be removed from list2, or just the first? - Mark Byers
-1 - I have downvoted every answer in this question because I thought they were all wrong, but it appears the question is just asked horribly. Now, I can't change them - apologies. Do you want to remove the items from list1 that exist in list2, or do you want to remove the items from list2 that exist in list1? At the time of this comment, each answer provided will perform the latter. - John Rasch
@John Rashch, you should be a little less trigger happy on those downvotes. Some of the answers are fairly conceptual and only demonstrate how to achieve what the OP wants without even relating to the lists mentioned in the question. - João Angelo
@Mark - you're right, my fault entirely - that's why I put the comment up here explaining what happened, I was searching for a previous answer I had already had to a similar question in the meantime after my voting and was going to leave comments after I found it - turns out that is not the best process for this! - John Rasch

9 Answers

421
votes

You can use Except:

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

You probably don't even need those temporary variables:

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

Note that Except does not modify either list - it creates a new list with the result.

46
votes

You don't need an index, as the List<T> class allows you to remove items by value rather than index by using the Remove function.

foreach(car item in list1) list2.Remove(item);
21
votes

I would recommend using the LINQ extension methods. You can easily do it with one line of code like so:

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

This is assuming of course the objects in list1 that you are removing from list2 are the same instance.

20
votes

In my case I had two different lists, with a common identifier, kind of like a foreign key. The second solution cited by "nzrytmn":

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

Was the one that best fit in my situation. I needed to load a DropDownList without the records that had already been registered.

Thank you !!!

This is my 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();
16
votes

You could use LINQ, but I would go with RemoveAll method. I think that is the one that better expresses your intent.

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

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

integers.RemoveAll(i => remove.Contains(i));
13
votes
list1.RemoveAll(l => list2.Contains(l));
8
votes

Solution 1 : You can do like this :

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

But in some cases may this solution not work. if it is not work you can use my second solution .

Solution 2 :

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

we pretend that list1 is your main list and list2 is your secondry list and you want to get items of list1 without items of list2.

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

As Except does not modify the list, you can use ForEach on List<T>:

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

It may not be the most efficient way, but it is simple, therefore readable, and it updates the original list (which is my requirement).

-4
votes

Here ya go..

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