Utilice LINQ para obtener elementos en una Lista , que no estén en otra Lista

Asumiría que hay una consulta LINQ simple para hacer esto, no estoy exactamente seguro de cómo. Por favor, vea el fragmento de código a continuación.

class Program { static void Main(string[] args) { List peopleList1 = new List(); peopleList1.Add(new Person() { ID = 1 }); peopleList1.Add(new Person() { ID = 2 }); peopleList1.Add(new Person() { ID = 3 }); List peopleList2 = new List(); 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 para darme todas las personas en peopleList2 que no están en peopleList1 este ejemplo debería darme dos personas (ID = 4 & ID = 5)

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

Si anulas la igualdad de las personas, también puedes usar:

 peopleList2.Except(peopleList1) 

Except debe 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 (casi) tienen un tiempo de ejecución de O(peopleList1.Count + peopleList2.Count) .

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

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

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

Esta variante no elimina duplicados.

O si lo quieres sin negación:

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

Básicamente dice get all from peopleList2 donde todos los ids en peopleList1 son diferentes de id en peoplesList2.

Solo un enfoque un poco diferente de la respuesta aceptada 🙂

Dado que todas las soluciones hasta la fecha utilizan syntax fluida, aquí hay una solución en la syntax de la 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 lo suficientemente diferente de las respuestas dadas para ser de interés para algunos, incluso si pensaban que sería muy poco óptimo para las listas. Ahora, para las tablas con ID indexados, este sería definitivamente el camino a seguir.

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

 List list1 = new List() { "1", "2", "3" }; List list2 = new List() { "2", "4" }; List 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(); List 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(); List 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(); 

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

La respuesta de Klaus fue genial, pero ReSharper le pedirá que “Simplifique la expresión de LINQ”:

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

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 usará para realizar la comparación.

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

Puedes usarlo de esta manera

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

Aquí hay un ejemplo de trabajo que permite obtener habilidades de TI que un candidato a trabajo ya no tiene.

 //Get a list of skills from the Skill table IEnumerable skillenum = skillrepository.Skill; //Get a list of skills the candidate has IEnumerable 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 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);