By JSprang


2010-10-15 18:00:28 8 Comments

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)

10 comments

@Wouter 2019-01-11 08:24:34

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

@Ángel Ibáñez 2018-11-27 09:02:34

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

@Brian T 2017-12-16 23:42:30

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

@Alrekr 2018-10-02 20:18:54

It's worth to note that this trick won't work if there's more than one property binding the two objects (think SQL composite key).

@Bertrand 2016-09-08 10:10:24

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

@Nicke Manarin 2019-07-24 13:23:15

By having the code that @BrianT has, how could I convert it to use your code?

@Richard Ockerby 2016-05-19 10:05:20

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

@user1271080 2015-11-20 17:37:55

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 :)

@DaveN 2017-07-23 08:59:37

This method (list of over 50,000 items) was significantly faster than the ANY method!

@Xtros 2018-06-11 06:23:50

This might be faster just because it is lazy. Note that this is not doing any real work just yet. It's not until you enumerate the list that it actually does the work (by calling ToList or using it as part of a foreach loop, etc.)

@Brian Quinn 2014-03-02 00:41:27

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

@CodesInChaos 2010-10-15 18:03:20

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.

@Klaus Byskov Pedersen 2010-10-15 18:05:03

That would only work if Equals had be overridden to compare ID's.

@JSprang 2010-10-15 18:07:40

Correct @klausbyskov, just tried this and I get 5 results.

@CodesInChaos 2010-10-15 18:10:33

That's why I wrote that you need to override the equality. But I've added an example which works even without that.

@Jon Hanna 2010-10-15 18:12:24

It would also work if Person was a struct. As it is though, Person seems an incomplete class as it has a property called "ID" which does not identify it - if it did identify it, then equals would be overridden so that equal ID meant equal Person. Once that bug in Person is fixed, this approach is then better (unless the bug is fixed by renaming "ID" to something else that doesn't mislead by seeming to be an identifier).

@Dan Korn 2017-10-04 19:26:02

It also works great if you're talking about a list of strings (or other base objects), which was what I was searching for when I came upon this thread.

@Maze 2018-05-10 08:28:41

@DanKorn Same, this a simpler solution, compared to the where, for basic comparison, int, objects ref, strings.

@Michael Goldshteyn 2010-10-16 22:04:16

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.

@user2099460 2019-11-18 20:54:42

Thank you. First answer that bothers with query expression syntax.

@Klaus Byskov Pedersen 2010-10-15 18:02:06

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

@Klaus Byskov Pedersen 2010-10-15 18:11:41

@JSprang, cool, you're welcome. You should mark my answer as the answer if it helped you (by clicking the check mark to the left), this way others can see that this was the correct answer (and I get more reputation ;-)

@Niki 2010-10-15 18:21:23

You are aware that that's a O(n*m) solution to a problem that can easily be solved in O(n+m) time?

@JSprang 2010-10-15 18:22:18

Yeah, it wouldn't let me mark it as the answer right away, said I needed to wait 5 minutes :) Thanks again!

@Klaus Byskov Pedersen 2010-10-15 18:27:41

@nikie, the OP asked for a solution that uses Linq. Maybe he's trying to learn Linq. If the question had been for the most efficient way, my question would not necessarily have been the same.

@Rubio 2014-09-02 06:25:00

@nikie, care to share your easy solution?

@AntonK 2016-06-21 01:30:52

This is equivalent and I find easier to follow: var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

@Menol 2016-11-02 10:58:15

@KlausByskovPedersen while your answer is spot on for the given question, please note that Google brings people like me who search for the same question without the "Use Linq" bit. I was already aware of where but was looking to see if there was a better solution. So would you consider adding the Except bit to your answer as well?

@Ernest 2017-07-06 21:18:39

You could also use .All(), making it slightly clearer, var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

@EzaBlade 2017-07-20 12:06:45

if in fact you use Resharper it will suggest you change the 'Any' to 'All' as mentioned above

@Chris Rogers 2017-09-11 23:46:05

@Menol - it might be a bit unfair to criticize someone who correctly responds to a question. People shouldn't need to anticipate all the ways and contexts that future people might stumble onto the answer. In reality, you should direct that to nikie - who took the time to state that they knew of an alternative without providing it.

@Menol 2017-09-20 08:15:22

@ChrisRogers I agree with your point. Just to be clear, I didn't mean to criticize Klaus, I was merely pointing him to the problem I faced hoping he would improve his ansewer. I do apologize if I didn't do a good job expressing it correctly.

@Casperonian 2018-10-01 00:17:46

Wow, you have to bring that "Google" thing into this huh?

@Yusha 2018-12-06 16:43:41

@Niki You are aware that this is not a time complexity question correct?

Related Questions

Sponsored Content

12 Answered Questions

[SOLVED] LINQ Aggregate algorithm explained

  • 2011-08-18 09:51:21
  • Alexander Beletsky
  • 242420 View
  • 688 Score
  • 12 Answer
  • Tags:   c# .net linq

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

23 Answered Questions

[SOLVED] LINQ query on a DataTable

13 Answered Questions

[SOLVED] When to use .First and when to use .FirstOrDefault with LINQ?

  • 2009-06-21 19:15:29
  • Metro Smurf
  • 537488 View
  • 796 Score
  • 13 Answer
  • Tags:   c# .net linq

15 Answered Questions

[SOLVED] Using LINQ to remove elements from a List<T>

  • 2009-05-12 15:56:24
  • TK.
  • 566860 View
  • 633 Score
  • 15 Answer
  • Tags:   c# .net linq list

7 Answered Questions

[SOLVED] Multiple "order by" in LINQ

  • 2008-11-18 13:34:11
  • Sasha
  • 574937 View
  • 1534 Score
  • 7 Answer
  • Tags:   linq sql-order-by

9 Answered Questions

[SOLVED] Group by in LINQ

  • 2011-09-06 19:44:20
  • test123
  • 1215136 View
  • 1001 Score
  • 9 Answer
  • Tags:   c# linq group-by

9 Answered Questions

[SOLVED] Concat all strings inside a List<string> using LINQ

  • 2009-02-18 00:56:57
  • Jobi Joy
  • 387792 View
  • 509 Score
  • 9 Answer
  • Tags:   c# linq .net-3.5

6 Answered Questions

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

  • 2016-05-23 10:51:42
  • ksg
  • 1407 View
  • 1 Score
  • 6 Answer
  • Tags:   c# linq

Sponsored Content