[c#] Use LINQ to get items in one List<>, that are not in another List<>

I would assume there's a simple LINQ query to do this, I'm just not exactly sure how.

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 would like to perform a LINQ query to give me all of the people in peopleList2 that are not in peopleList1.

This example should give me two people (ID = 4 & ID = 5)

This question is related to c# linq .net-3.5

The answer is


This can be addressed using the following LINQ expression:

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

An alternate way of expressing this via LINQ, which some developers find more readable:

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

Warning: As noted in the comments, these approaches mandate an O(n*m) operation. That may be fine, but could introduce performance issues, and especially if the data set is quite large. If this doesn't satisfy your performance requirements, you may need to evaluate other options. Since the stated requirement is for a solution in LINQ, however, those options aren't explored here. As always, evaluate any approach against the performance requirements your project might have.


Here is a working example that get IT skills that a job candidate does not already have.

//Get a list of skills from the Skill table
IEnumerable<Skill> skillenum = skillrepository.Skill;
//Get a list of skills the candidate has                   
IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill
       .Where(p => p.Candidate_ID == Candidate_ID);             
//Using the enum lists with LINQ filter out the skills not in the candidate skill list
IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID));
//Assign the selectable list to a viewBag
ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1);

Bit late to the party but a good solution which is also Linq to SQL compatible is:

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


first, extract ids from the collection where condition

List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();

second, use "compare" estament to select ids diffent to the selection

List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();

Obviously you can use x.key != "TEST", but is only a example


Or if you want it without negation:

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

Basically it says get all from peopleList2 where all ids in peopleList1 are different from id in peoplesList2.

Just a little bit different approach from the accepted answer :)


If you override the equality of People then you can also use:

peopleList2.Except(peopleList1)

Except should be significantly faster than the Where(...Any) variant since it can put the second list into a hashtable. Where(...Any) has a runtime of O(peopleList1.Count * peopleList2.Count) whereas variants based on HashSet<T> (almost) have a runtime of O(peopleList1.Count + peopleList2.Count).

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

Or if you want fast code but don't want to override the equality:

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

This variant does not remove duplicates.


Once you write a generic FuncEqualityComparer you can use it everywhere.

peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));

public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, int> hash;

    public FuncEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
        if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
            hash = (_) => 0;
        else
            hash = t => t.GetHashCode(); 
    }

    public bool Equals(T x, T y) => comparer(x, y);
    public int GetHashCode(T obj) => hash(obj);
}

Since all of the solutions to date used fluent syntax, here is a solution in query expression syntax, for those interested:

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

I think it is different enough from the answers given to be of interest to some, even thought it most likely would be suboptimal for Lists. Now for tables with indexed IDs, this would definitely be the way to go.


Klaus' answer was great, but ReSharper will ask you to "Simplify LINQ expression":

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


This Enumerable Extension allow you to define a list of item to exclude and a function to use to find key to use to perform comparison.

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source,
    IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector)
    {
       var excludedSet = new HashSet<TKey>(exclude.Select(keySelector));
       return source.Where(item => !excludedSet.Contains(keySelector(item)));
    }
}

You can use it this way

list1.Exclude(list2, i => i.ID);

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to linq

Async await in linq select How to resolve Value cannot be null. Parameter name: source in linq? What does Include() do in LINQ? Selecting multiple columns with linq query and lambda expression System.Collections.Generic.List does not contain a definition for 'Select' lambda expression join multiple tables with select and where clause LINQ select one field from list of DTO objects to array The model backing the 'ApplicationDbContext' context has changed since the database was created Check if two lists are equal Why is this error, 'Sequence contains no elements', happening?

Examples related to .net-3.5

Get first element from a dictionary redistributable offline .NET Framework 3.5 installer for Windows 8 How to read an entire file to a string using C#? HTTP Error 500.22 - Internal Server Error (An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode.) Change the Textbox height? How to get current date in 'YYYY-MM-DD' format in ASP.NET? C# Numeric Only TextBox Control Will the IE9 WebBrowser Control Support all of IE9's features, including SVG? Use LINQ to get items in one List<>, that are not in another List<> Get Absolute URL from Relative path (refactored method)