Mengenoperationen mit LINQ

Wie weithin bekannt ist, kann man mit LINQ (Language Integrated Query) beliebige Datenquellen abfragen. Ich bin vor kurzem auf eine weitere interessante Anwendungsmöglichkeit gestossen, die wohl nicht allen bekannt ist: Mengenoperationen.

Ich fand diese Möglichkeit als ich nach einer optimierten Version für so ein Stück Code suchte:

private static List<int> ValuesNotInSecondList(List<int> first, List<int> second)
{
    List<int> result = new List<int>();

    foreach (var i in first)
    {
        if(!second.Contains(i))
        {
            result.Add(i);
        }
    }

    return result;
}

Aus einer Liste A sollen alle Werte zurückgeliefert werden, die nicht in Liste B vorhanden sind. Diese an sich einfache Aufgabe braucht nach meinem Geschmack zu viel Code.

ReSharper hat mir diese optimierte Version vorgeschlagen:

private static List<int> ValuesNotInSecondList(List<int> first, List<int> second)
{
    return first.Where(i => !second.Contains(i)).ToList();
}

Der Code wurde schon massiv reduziert, aber irgendwie genügte mir dies noch nicht. Da in meinem konkreten Anwendungsfall in meiner ersten Liste die Werte immer nur 1x vorkommen, suchte ich weiter.

 
Differenz zweier Mengen
In Wikipedia wird die (mathematische) Differenz zweier Mengen wie folgt beschrieben:

Die Differenzmenge (auch Restmenge) von A und B ist die Menge der Elemente, die in A, aber nicht in B enthalten sind.

Und das ist eigentlich genau das, was die Methode machen soll. LINQ erweitert IEnumerable um die Methode Except, die genau dieses Verhalten implementiert:

List<int> listOdd = new List<int> { 1, 3, 5, 7, 9 };
List<int> listFibonacci = new List<int> { 1, 2, 3, 5, 8 };

List<int> resultExcept = listOdd.Except(listFibonacci).ToList();
PrintList(resultExcept); // Resultat: 7,9

In diesem Beispiel werden die Zahlen zurückgegeben, die zwar ungerade aber nicht teil meiner reduzierten Fibonacci-Folge sind.

 
Schnittmenge und Vereinigung
LINQ bietet ebenfalls eine Methode für die Schnittmenge (Intersect) und die Vereinigung (Union) an:

List<int> resultIntersect = listOdd.Intersect(listFibonacci).ToList();
PrintList(resultIntersect); // Resultat: 1,3,5

List<int> resultUnion = listOdd.Union(listFibonacci).OrderBy(i => i).ToList();
PrintList(resultUnion); // Resultat: 1,2,3,5,7,8,9 (Reihenfolge durch die Sortierung)

 
Auch mit Strings möglich
Was so schön mit Integer funktionierte, geht übrigens genauso auch mit Strings (oder jedem anderen Typ der mit EqualityComparer.Default verglichen werden kann IEquatable implementiert):

List<string> listAD = new List<string> { "A", "B", "C", "D" };
List<string> listCF = new List<string> { "C", "D", "E", "F", };


List<string> resultADexceptCF = listAD.Except(listCF).ToList();
PrintList(resultADexceptCF); // Resultat: A,B

List<string> resultADintersectCF = listAD.Intersect(listCF).ToList();
PrintList(resultADintersectCF); // Resultat: C,D

List<string> resultADunionCF = listAD.Union(listCF).ToList();
PrintList(resultADunionCF); // Resultat: A,B,C,D,E,F

 
Fazit
Für Mengenoperationen bietet LINQ spezielle Operationen an, die einem viel eigenen Code ersparen. Wichtig ist dabei aber, dass die Listen nicht mehrmals den gleichen Wert enthalten. Sonst sind es keine Mengen mehr und LINQ filtert die doppelten Werte in der Resultatmenge selber heraus.

6 Gedanken zu „Mengenoperationen mit LINQ“

  1. Hi Johnny

    So und nun die grosse Preisfrage: Welche Schnittstelle muss implementiert werden, damit eine Mengenoperation wie Except auf eigene Objekte angewendet werden kann?

    Gruss

    René

  2. Hallo René,
    So wie ich die Dokumentation verstehe, sollte IEnumerable genügen. Except ist eine Extension Method auf IEnumerable und wenn der Namespace System.Linq referenziert wird, müsste das nach mir funktionieren.

    Falls ich damit falsch liege korrigiere mich bitte.

    Gruss Johnny

    1. Hallo Renè,
      Danke für den Hinweis, habe den Eintrag entsprechend korrigiert. Zudem ist eine Mail wegen dem Community Feed unterwegs.😉

      Gruss Johnny

Kommentare sind geschlossen.