web-dev-qa-db-fra.com

Utilisez LINQ pour obtenir des éléments dans une liste <>, qui ne sont pas dans une autre liste <>

Je suppose qu'il existe une simple requête LINQ pour le faire, je ne sais pas exactement comment. S'il vous plaît voir l'extrait de code ci-dessous.

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

Je voudrais effectuer une requête LINQ pour me donner toutes les personnes dans peopleList2 qui ne sont pas dans peopleList1 cet exemple devrait me donner deux personnes (ID = 4 & ID = 5)

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

Si vous substituez l'égalité des personnes, vous pouvez également utiliser:

peopleList2.Except(peopleList1)

Except devrait être nettement plus rapide que la variante Where(...Any) car elle peut placer la deuxième liste dans une table de hachage. Where(...Any) a une durée d'exécution de O(peopleList1.Count * peopleList2.Count) alors que les variantes basées sur HashSet<T> (presque) ont une durée d'exécution de O(peopleList1.Count + peopleList2.Count).

Except supprime implicitement les doublons. Cela ne devrait pas affecter votre cas, mais pourrait être un problème pour des cas similaires.

Ou si vous voulez un code rapide mais que vous ne voulez pas remplacer l'égalité:

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

Cette variante ne supprime pas les doublons.

318
CodesInChaos

Ou si vous le voulez sans négation:

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

Fondamentalement, il est dit que tout doit être extrait de peopleList2 où tous les identifiants de peopleList1 sont différents de id de peoplesList2. 

Juste une approche un peu différente de la réponse acceptée :)

53
user1271080

Comme toutes les solutions utilisées à ce jour utilisaient une syntaxe fluide, voici une solution de syntaxe d'expression de requête destinée aux personnes intéressées:

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

Je pense que cela est suffisamment différent des réponses données pour intéresser certains, même si cela serait probablement très optimiste pour les listes. Maintenant, pour les tables avec des ID indexés, ce serait vraiment la voie à suivre.

28
Michael Goldshteyn

Un peu en retard pour le parti mais une bonne solution qui est également compatible Linq to SQL est:

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

Félicitations à http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-with-C

12
Richard Ockerby

Cette extension énumérable vous permet de définir une liste d'éléments à exclure et une fonction à utiliser pour rechercher la clé à utiliser pour effectuer une comparaison.

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

Vous pouvez l'utiliser de cette façon

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

Une fois que vous avez écrit un FuncEqualityComparer générique, vous pouvez l’utiliser partout.

peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));

public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, int> hash;

    public FuncEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
        if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
            hash = (_) => 0;
        else
            hash = t => t.GetHashCode(); 
    }

    public bool Equals(T x, T y) => comparer(x, y);
    public int GetHashCode(T obj) => hash(obj);
}
0
Wouter

Voici un exemple de travail qui permet d’acquérir des compétences en informatique qu’un candidat à un poste ne possède pas déjà.

//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);
0
Brian Quinn

d’abord, extrayez les identifiants de la collection où condition

List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();

deuxièmement, utilisez le paramètre "compare" pour sélectionner des identifiants différents de ceux sélectionnés

List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();

Évidemment, vous pouvez utiliser x.key! = "TEST", mais ce n’est qu’un exemple.

0
Ángel Ibáñez