it-swarm.com.de

Verwenden Sie LINQ, um Elemente in einer Liste <> abzurufen, die sich nicht in einer anderen Liste <> befinden

Ich würde davon ausgehen, dass es eine einfache LINQ-Abfrage gibt, ich bin nur nicht ganz sicher, wie. Bitte sehen Sie den Code-Ausschnitt unten.

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

Ich möchte eine LINQ-Abfrage durchführen, um mir alle Personen in peopleList2 zu geben, die sich nicht in peopleList1 befinden. Dieses Beispiel sollte mir zwei Personen (ID = 4 & ID = 5)

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

Wenn Sie die Gleichheit der Personen überschreiben, können Sie auch Folgendes verwenden:

peopleList2.Except(peopleList1)

Except sollte deutlich schneller als die Where(...Any)-Variante sein, da sie die zweite Liste in eine Hashtabelle setzen kann. Where(...Any) hat eine Laufzeit von O(peopleList1.Count * peopleList2.Count), wohingegen Varianten, die auf HashSet<T> (fast) basieren, eine Laufzeit von O(peopleList1.Count + peopleList2.Count) haben.

Except entfernt implizit Duplikate. Das sollte sich nicht auf Ihren Fall auswirken, könnte aber für ähnliche Fälle ein Problem sein.

Oder wenn Sie schnellen Code wünschen, die Gleichheit jedoch nicht überschreiben möchten:

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

Diese Variante entfernt keine Duplikate.

318
CodesInChaos

Oder wenn Sie es ohne Verneinung wollen:

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

Grundsätzlich heißt es, holen Sie sich alle von peopleList2, wobei sich alle IDs in peopleList1 von id in peopleList2 unterscheiden. 

Nur ein bisschen anders als die akzeptierte Antwort :)

53
user1271080

Da alle Lösungen bis jetzt eine fließende Syntax verwendet haben, finden Sie hier eine Lösung in der Abfrageausdruckssyntax:

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

Ich denke, es unterscheidet sich genug von den Antworten, um für einige von Interesse zu sein, selbst wenn man bedenkt, dass es höchstwahrscheinlich für Listen suboptimal wäre. Für Tabellen mit indizierten IDs wäre dies definitiv der richtige Weg.

28

Etwas spät zur Party, aber eine gute Lösung, die auch Linq to SQL kompatibel ist, ist:

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

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

12
Richard Ockerby

Klaus 'Antwort war großartig, aber ReSharper fordert Sie auf, den LINQ-Ausdruck zu vereinfachen:

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

10
Brian T

Mit dieser Enumerable-Erweiterung können Sie eine Liste von Elementen definieren, die ausgeschlossen werden sollen, sowie eine Funktion, mit der der Schlüssel für den Vergleich gesucht wird.

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

Sie können es so verwenden

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

Sobald Sie einen generischen FuncEqualityComparer schreiben, können Sie ihn überall verwenden.

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

Hier ein Arbeitsbeispiel, das IT-Kenntnisse vermittelt, über die ein Bewerber nicht verfügt.

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

extrahieren Sie zunächst IDs aus der Sammlung, wo die Bedingung gilt

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

zweitens: Verwenden Sie "Compare", um die zur Auswahl unterschiedlichen IDs auszuwählen

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

Natürlich können Sie x.key! = "TEST" verwenden, ist aber nur ein Beispiel

0
Ángel Ibáñez