57 Stimmen

C#: Inhalt von zwei IEnumerables vergleichen

Gibt es eine eingebaute Linq-Methode, die ich verwenden kann, um herauszufinden, ob zwei Sequenzen die gleichen Elemente enthält, ohne die Reihenfolge zu berücksichtigen?

Zum Beispiel:

{1, 2, 3} == {2, 1, 3}
{1, 2, 3} != {2, 1, 3, 4}
{1, 2, 3} != {1, 2, 4}

Sie haben die SequenceEquals, aber dann müsste ich erst beide Sequenzen bestellen, nicht wahr?

50voto

leppie Punkte 111830

Dafür gibt es mehrere Möglichkeiten. Angenommen, A und B ist IEnumerable.

!A.Except(B).Any() && !B.Except(A).Any()
A.Count() == B.Count() && A.Intersect(B).Count() == B.Count()
etc

12voto

Jon Hanna Punkte 106367

Wenn Sie sich nicht um Duplikate kümmern (d. h. Sie würden die {1, 2, 3} gleichzusetzen mit {1, 2, 3, 2} ) dann:

new HashSet<int>(A).SetEquals(B)

(Oder welcher Typ auch immer der Elementtyp ist anstelle von int ).

Ansonsten:

public static bool SequenceEqualUnordered<T>(IEnumerable<T> first, IEnumerable<T> second)
{
    if (first == null)
        return second == null; // or throw if that's more appropriate to your use.
    if (second == null)
        return false;   // likewise.
    var dict = new Dictionary<T, int>(); // You could provide a IEqualityComparer<T> here if desired.
    foreach(T element in first)
    {
        int count;
        dict.TryGetValue(element, out count);
        dict[element] = count + 1;
    }
    foreach(T element in second)
    {
        int count;
        if (!dict.TryGetValue(element, out count))
            return false;
        else if (--count == 0)
            dict.Remove(element);
        else
            dict[element] = count;
    }
    return dict.Count == 0;
}

Notieren Sie jedes Element der ersten Sequenz und vergleichen Sie dann die zweite mit ihr. In dem Moment, in dem Sie ein Element zu viel in der zweiten Sequenz haben, können Sie false zurückgeben, andernfalls, wenn Sie nichts mehr im Wörterbuch der Zählungen haben, sind sie gleich, oder false, wenn es irgendwelche Elemente übrig gibt.

Anstelle der beiden O(n log n)-Arten der Verwendung von OrderBy() gefolgt von einem O(n)-Vergleich, haben Sie eine O(n)-Operation, die die Menge der Tallies aufbaut, und eine O(n)-Prüfung gegen sie.

10voto

Yoann Punkte 111

Mit zwei IEnumerables (A und B) :

bool equal = (A.Count() == B.Count() && (!A.Except(B).Any() || !B.Except(A).Any()))

Ich denke, dies ist besser als Except(A).Count, weil nicht die gesamte Excep ausgewertet wird. Es wird angehalten, sobald ein Element im Except gefunden wird. Mit Count wird der gesamte Except ausgewertet. Darüber hinaus können wir die Auswertung dieser kostspieligen Except vermeiden, indem wir zuerst die Count-Eigenschaften überprüfen. Wenn Counts nicht gleich sind, prüfen wir die Excepts.

4voto

Rauhotz Punkte 7594

Versuchen Sie die Klasse HashSet:

var enumA = new[] { 1, 2, 3, 4 };
var enumB = new[] { 4, 3, 1, 2 };

var hashSet = new HashSet<int>(enumA);
hashSet.SymmetricExceptWith(enumB);
Console.WriteLine(hashSet.Count == 0); //true => equal

Das funktioniert aber nur dann korrekt, wenn die Werte eindeutig sind.

Zum Beispiel

var enumA = new[] { 1, 1, 1, 2 };
var enumB = new[] { 1, 2, 2, 2 };

werden mit der genannten Methode ebenfalls als "gleich" betrachtet.

2voto

Um bei Ihrem Beispiel zu bleiben, können Sie beide IEnumerable zum Typ List machen und dann SequenceEqual wie im folgenden Beispiel verwenden:

var first = Enumerable.Range(1, 3);
var second = Enumerable.Range(1, 3);
var areTheyEqual = first.ToList().SequenceEqual(second.ToList());
if (areTheyEqual)
{ /* do something... */}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X