Use o LINQ para obter itens em uma lista , que não estão em outra lista

Eu diria que há uma consulta LINQ simples para fazer isso, não sei exatamente como. Por favor, veja o trecho de código abaixo.

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

Eu gostaria de realizar uma consulta LINQ para me dar todas as pessoas em peopleList2 que não estão em peopleList1 este exemplo deve me dar duas pessoas (ID = 4 & ID = 5)

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

Se você replace a igualdade de pessoas, também poderá usar:

 peopleList2.Except(peopleList1) 

Except deve ser significativamente mais rápido que a variante Where(...Any) pois pode colocar a segunda lista em um hashtable. Where(...Any) tem um tempo de execução de O(peopleList1.Count * peopleList2.Count) enquanto as variantes baseadas em HashSet (quase) possuem um tempo de execução de O(peopleList1.Count + peopleList2.Count) .

Except implicitamente remove duplicados. Isso não deve afetar seu caso, mas pode ser um problema para casos semelhantes.

Ou se você quiser código rápido, mas não quer sobrescrever a igualdade:

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

Essa variante não remove duplicatas.

Ou se você quiser sem negação:

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

Basicamente ele diz get all de peopleList2 onde todos os ids em peopleList1 são diferentes de id em peoplesList2.

Apenas uma abordagem um pouco diferente da resposta aceita 🙂

Como todas as soluções usadas até hoje usavam uma syntax fluente, aqui está uma solução na syntax de expressão de consulta, para os interessados:

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

Eu acho que é diferente o suficiente das respostas dadas para ser de interesse para alguns, até pensei que provavelmente seria abaixo do ideal para listas. Agora, para tabelas com IDs indexadas, esse definitivamente seria o caminho a ser seguido.

Pouco tarde para a festa, mas uma boa solução que também é compatível com o Linq to SQL é:

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

Parabéns para http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C

A resposta de Klaus foi ótima, mas o ReSharper pedirá para você “Simplificar a expressão do LINQ”:

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

Esta Extensão Enumerável permite que você defina uma lista de itens a serem excluídos e uma function a ser usada para localizar a chave a ser usada para realizar a comparação.

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

Você pode usar dessa maneira

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

Aqui está um exemplo prático que obtém habilidades em TI que um candidato a emprego ainda não possui.

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