2010-10-15 23 views
338

Supongo que hay una consulta LINQ simple para hacer esto, no estoy exactamente seguro de cómo hacerlo. Por favor, vea el fragmento de código a continuación.Use LINQ para obtener elementos en una Lista <>, que no están en otra Lista <>

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

me gustaría realizar una consulta LINQ que darme toda la gente en peopleList2 que no están en peopleList1 este ejemplo debería darme dos personas (ID = 4 & ID = 5)

+2

Tal vez sea un buen idea para hacer ID de forma legible, ya que la identidad de un objeto no debería cambiar durante su tiempo de transmisión. A menos que, por supuesto, su marco de prueba o ORM requiera que sea mutable. – CodesInChaos

+0

¿Podríamos llamar a esto "Izquierda (o Derecha) Excluir Únase" de acuerdo con [este diagrama?] (Https://www.codeproject.com/Articles/33052/Representation-Visual-de-SQL-Joins) –

Respuesta

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

@JSprang , genial, de nada. Debes marcar mi respuesta como * la respuesta * si te ayudó (haciendo clic en la marca de verificación a la izquierda), de esta manera otros pueden ver que esta fue la respuesta correcta (y obtengo más reputación ;-) –

+26

Eres consciente de que esa es una solución O (n * m) a un problema que puede resolverse fácilmente en el tiempo O (n + m)? – Niki

+0

Sí, no me dejó marcarlo como la respuesta inmediata, dije que necesitaba esperar 5 minutos :) ¡Gracias de nuevo! – JSprang

260

Si anula la igualdad de las personas, entonces también puede utilizar:

peopleList2.Except(peopleList1) 

Except, deben ser significativamente más rápido que la variante Where(...Any) ya que puede poner la segunda lista en una tabla hash. Where(...Any) tiene un tiempo de ejecución de O(peopleList1.Count * peopleList2.Count) mientras que las variantes basadas en HashSet<T> (casi) tienen un tiempo de ejecución de O(peopleList1.Count + peopleList2.Count).

Except elimina implícitamente los duplicados. Eso no debería afectar su caso, pero podría ser un problema para casos similares.

O si desea código rápido, pero no quiere anular la igualdad:

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

Esta variante no quita los duplicados.

+0

Eso solo funcionaría si 'Igual' hubiera sido reemplazado para comparar ID's. –

+0

Corregir @klausbyskov, acabo de probar esto y obtengo 5 resultados. – JSprang

+16

Es por eso que escribí que debes anular la igualdad. Pero he agregado un ejemplo que funciona incluso sin eso. – CodesInChaos

28

Puesto que todas las soluciones hasta la fecha se utilizan sintaxis fluidez, aquí es una solución en la sintaxis de expresión de consulta, para los interesados:

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

Creo que es bastante diferente de las respuestas dadas a ser de su interés algunos, incluso pensaron que sería muy poco óptimo para las listas. Ahora, para las tablas con ID indexados, este sería definitivamente el camino a seguir.

0

Aquí hay un ejemplo de trabajo que tiene las habilidades de TI que un candidato no tiene .

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

O si lo desea, sin negación:

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

Básicamente se dice conseguir todo desde peopleList2 donde todos los identificadores en peopleList1 son diferentes de id en peoplesList2.

Sólo un enfoque poco diferente de la respuesta aceptada :)

+2

Este método (lista de más de 50,000 artículos) fue significativamente más rápido que el método ANY. – DaveN

10

poco tarde a la fiesta, pero una buena solución que también es compatible con LINQ to SQL es:

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

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

4

Esta extensión enumerable le permite definir una lista de elementos para excluir y una función para usar para encontrar la clave que se utilizará para realizar la comparación.

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

Usted puede usarlo de esta manera

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

respuesta de Klaus era grande, pero ReSharper le pedirá que "Simplificar la expresión LINQ":

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

Cuestiones relacionadas