Coder Perfect

Use LINQ to get items in one List<>, that are not in another List<>

Problem

I’m sure there’s an easy LINQ query for this, but I’m not sure how to do it.

Given this piece of code:

class Program
{
    static void Main(string[] args)
    {
        List<Person> peopleList1 = new List<Person>();
        peopleList1.Add(new Person() { ID = 1 });
        peopleList1.Add(new Person() { ID = 2 });
        peopleList1.Add(new Person() { ID = 3 });

        List<Person> peopleList2 = new List<Person>();
        peopleList2.Add(new Person() { ID = 1 });
        peopleList2.Add(new Person() { ID = 2 });
        peopleList2.Add(new Person() { ID = 3 });
        peopleList2.Add(new Person() { ID = 4 });
        peopleList2.Add(new Person() { ID = 5 });
    }
}

class Person
{
    public int ID { get; set; }
}

I’d like to use LINQ to get a list of all the people in peopleList2 who aren’t in peopleList1.

This example should provide me with two individuals. (IDs = 4 and 5)

Asked by JSprang

Solution #1

The following LINQ expression can be used to solve this problem:

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

Another approach to express this using LINQ that some developers find more readable:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

Answered by Klaus Byskov Pedersen

Solution #2

You can also use: if you want to overcome the equality of people.

peopleList2.Except(peopleList1)

Because it can put the second list into a hashtable, Except should be substantially faster than the Where(…Any) option. Where(…Any) has an O(peopleList1.Count * peopleList2.Count) runtime, whereas HashSetT> versions (nearly) have an O(peopleList1.Count + peopleList2.Count) runtime.

Except implicitly removes duplicates. That shouldn’t affect your case, but might be an issue for similar cases.

If you want fast code but don’t want to override the equality, you can use the following syntax:

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID));
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID));

Duplicates are not removed with this variant.

Answered by CodesInChaos

Solution #3

Alternatively, if you want it without the negation:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

Essentially, it states to obtain all from peopleList2, where all of the ids in peopleList1 differ from the id in peoplesList2.

A little different method from the standard response:)

Answered by user1271080

Solution #4

Because all of the previous solutions employed fluid syntax, here is a query expression syntax solution for those who are interested:

var peopleDifference = 
  from person2 in peopleList2
  where !(
      from person1 in peopleList1 
      select person1.ID
    ).Contains(person2.ID)
  select person2;

Even though it would most certainly be optimum for Lists, I believe it is distinct enough from the answers offered to be of interest to some. This would absolutely be the way to go for tables with indexed IDs.

Answered by Michael Goldshteyn

Solution #5

Although I’m a little late to the game, here’s an excellent solution that’s also Linq to SQL compatible:

List<string> list1 = new List<string>() { "1", "2", "3" };
List<string> list2 = new List<string>() { "2", "4" };

List<string> inList1ButNotList2 = (from o in list1
                                   join p in list2 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inList2ButNotList1 = (from o in list2
                                   join p in list1 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inBoth = (from o in list1
                       join p in list2 on o equals p into t
                       from od in t.DefaultIfEmpty()
                       where od != null
                       select od).ToList<string>();

Kudos to http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C

Answered by Richard Ockerby

Post is based on https://stackoverflow.com/questions/3944803/use-linq-to-get-items-in-one-list-that-are-not-in-another-list