tags:

views:

81

answers:

3

I would assume there's a simple LINQ query to do this, I'm just not exactly sure how. Please see code snippet below, the comment explains what I'd like to do:

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

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

class Person
{
    public int ID { get; set; }
}
+4  A: 
var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));
klausbyskov
This is exactly what I needed! Thanks for the quick response :)
JSprang
@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 ;-)
klausbyskov
You are aware that that's a O(n*m) solution to a problem that can easily be solved in O(n+m) time?
nikie
Yeah, it wouldn't let me mark it as the answer right away, said I needed to wait 5 minutes :) Thanks again!
JSprang
@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.
klausbyskov
+8  A: 

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 (almost) have a runtime of O(peopleList1.Count+peopleList2.Count).

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));
CodeInChaos
That would only work if `Equals` had be overridden to compare ID's.
klausbyskov
Correct @klausbyskov, just tried this and I get 5 results.
JSprang
That's why I wrote that you need to override the equality. But I've added an example which works even without that.
CodeInChaos
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).
Jon Hanna
A: 

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.

Michael Goldshteyn