2011-01-04 2 views
4

Nehmen wir an, wir zwei Kollektionen, mindestens IEnumerable an die Macht auf Linq (auch davon ausgehen, .net 4.0):Gleichheit für 2 Listen verschiedener Typen

List<T1> list1; 
List<T2> list2; 

Ich kann die Gleichstellung von Objekten des Typs T1 und T2 definieren .

  1. Was ist der beste Weg ist, (das heißt .net-Schnittstelle und Linq bevorzugt), um zu überprüfen, ob die 2 Listen gleich sind (Reihenfolge der Elemente ist nicht relevant).

  2. Wie kann ich dieses Problem zu optimieren, wenn ich, dass die Objekte T1 und T2 weiß

    eine ID

Ex von T1 und T2:

class Device 
{ 
    string Id; 
    string Name; 
} 

class DeviceInfo 
{ 
    string Identifier; 
    string FriendlyName; 
    DateTime CreateDate; 
} 

Später bearbeiten:

Die Lösung sollte eine Art Gleichheitsvergleich beinhalten, den ich schreibe und generisch genug ist. Es kann Fälle geben, in denen 2 Objekte die gleiche ID, aber einen anderen Namen haben, und der Vergleich sollte dann fehlschlagen. Zum Beispiel:

static bool AreEqual(Device device, DeviceInfo deviceInfo) 
{ 
    return device.Id == deviceInfo.Identifier && 
      device.Name == deviceInfo.FriendlyName; 
} 
+0

'IEqualityComparer' soll an Objekten des gleichen Typs arbeiten. Sie können es so einrichten, dass es zwei "Objekt" -Parameter akzeptiert und einige Castings oder ähnliches versucht, aber nicht: Verwenden Sie das richtige Werkzeug für den Job. – Jon

Antwort

3

4.0 .NET Angenommen:

Foo[] foos = new Foo[]; 
Bar[] bars = new Bar[]; 

var areDifferent = foos.Zip(bars, (foo, bar) => foo.Id == bar.Id).Any(b => !b); 

Eine bessere Lösung wäre auch prüfen, ob foosbars und die gleiche Länge haben und daß keines der Elemente sind null sowie. Und dieses Beispiel nimmt natürlich an, dass die Sammlungen bereits nach Id sortiert sind.

Update:

So, hier ist die "bessere Lösung" in all seinen LINQy Detail:

var areDifferent = foos.Count() != bars.Count() || 
        foos.OrderBy(foo => foo.Id) 
        .Zip(
         bars.OrderBy(bar => bar.Id), 
         (foo, bar) => foo != null && bar != null && foo.Id == bar.Id) 
        .Any(b => !b); 
+0

Das OP sagt "Reihenfolge der Elemente ist nicht relevant". Dies ergibt falsche negative Ergebnisse, wenn die Sequenzen äquivalent, aber unterschiedlich geordnet sind. – Ani

+0

@Ani: Sicher, ich war noch nicht fertig mit der Bearbeitung der Antwort. :) – Jon

+0

Zip funktioniert, aber ich brauchte ein paar Minuten, um den Code zu lesen. Warum nicht SequenceEquals? –

2

Man könnte so etwas tun:

List<Device> devices = ... 
List<DeviceInfo> deviceInfos = ... 

var deviceIds = devices.Select(d => d.Id) 
         .OrderBy(id => id); 

var deviceInfoIds = deviceInfos.Select(d => d.Identifier) 
           .OrderBy(id => id); 

bool areEqual = deviceIds.SequenceEqual(deviceInfoIds); 

Wenn doppeltes Ids nicht möglich ist, Set Semantik in handlichen kommt:

bool areEqual = !devices.Select(d => d.Id) 
         .Except(deviceInfos.Select(d => d.Identifier)) 
         .Any(); 

ich, wenn möglich, empfehlen würde dass Sie eine IHasId (oder eine ähnliche) Schnittstelle deklarieren und beide Typen zur Implementierung erhalten.

EDIT:

Als Antwort auf Ihre bearbeiten Sie könnte schreiben Sie eine IEqualityComparer Implementierung, die tat, was Sie wollten. Es würde wirklich hässlich aussehen; Sie müssten eine spekulative Umwandlung von jede Argument zu DeviceInfo/Device zu versuchen, und versuchen, eine Kennung heraus zu extrahieren. Ich würde das nicht wirklich empfehlen; Es ist eine schlechte Idee für einen Gleichheitsvergleich, Objekte ganz unterschiedlicher Art zu vergleichen. Es wäre ein Los einfacher, wenn Sie jeden Typ haben, um eine allgemeine Schnittstelle zu implementieren, die einen Bezeichner zur Verfügung stellte.

+0

Sie haben das Problem reduziert, 2 Strings zu vergleichen. Wenn nicht IEquatable /IEqualityComparer, was sonst? –

0

Vergleich 2 Listen von Strings ist nicht sehr kompliziert. Beide Lösungen, die auf der Reihenfolge der Listen basieren, haben eine N log (N) -Komplexität, die die Größe der Zeichenfolge nicht berücksichtigt. Die bessere Lösung ist (Pseudo-Code), die Komplexität ist N:

create a dictionary<string, int> 

foreach element in list1 
if element is in dict 
dict[element]++; 
else 
dict[element] = 1; 

foreach element in list2 
if element is in dict 
dict[element]--; 
else 
return NOT_EQUAL; 

if dict has only 0 values lists are equal 
0

dieses Versuchen Sie, den Unterschied zwischen zwei verschiedenen Listen zu erhalten: Wenn sie keine gemeinsame Eigenschaft haben.

var differentItems = List<Type1>.Select(d => d.Name) 
         .Except(List<Type2>.Select(d => d.Name));