c# linq .net-3.5

c# - Use LINQ para obtener elementos en una Lista<>, que no están en otra Lista<>



.net-3.5 (8)

Supongo que hay una consulta LINQ simple para hacer esto, pero 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<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 para darme todas las personas en peopleList2 que no están en peopleList1 Este ejemplo debería darme dos personas (ID = 4 y ID = 5)


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

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


Dado que todas las soluciones hasta la fecha utilizan sintaxis fluida, aquí hay una solución en la sintaxis de expresión de consulta, para aquellos 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 aunque probablemente sea subóptima para las listas. Ahora para las tablas con identificadores indexados, este sería definitivamente el camino a seguir.


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

Puedes usarlo de esta manera

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


La respuesta de Klaus fue excelente, pero ReSharper le pedirá que "Simplifique la expresión de LINQ":

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


O si lo quieres sin negación:

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

Básicamente dice que se obtiene todo de peopleList2, donde todas las identificaciones en peopleList1 son diferentes de id en peoplesList2.

Solo un poco diferente de la respuesta aceptada :)


Poco tarde en 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


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

peopleList2.Except(peopleList1)

Except debería ser significativamente más rápido que la variante Where(...Any) ya que puede colocar 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 implícitamente elimina los duplicados. Eso no debería afectar su caso, pero podría ser un problema para casos similares.

O si quieres un código rápido pero no quieres 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 elimina duplicados.


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