[vb.net] List(of T) miteinander Vergleichen
Gepostet am: Jun 13, 2011 7:29:40 PM
Ausgangsbasis ist eine sauber erstellte Klasse:
Public Class Auto
Public Class Auto Private _marke As String = String.Empty Private _modell As String = String.Empty Public Sub New(ByVal marke As String, ByVal modell As String) Me._marke = marke Me._modell = modell End Sub Public Property Marke() As String Get Return _marke End Get Set(ByVal value As String) Me._marke = value End Set End Property Public Property Modell() As String Get Return _modell End Get Set(ByVal value As String) Me._modell = value End Set End PropertyEnd Class
Hat man nun zwei Listen dieser Klasse und möchte diese miteinander vergleichen, gibt es verschiedene Möglichkeiten, mit unterschiedlichen Ergebnis.
Vergleich der Referenz
Vergleich mit .Contains
Will man aus den 2 Listen eine weitere Liste erstellen, die nur die Elemente enthält, die in beiden vorkommen, gibt es folgende Möglichkeit:
Compare_with_Contains
Dim Autos3 As New List(Of Auto)For Each myAuto As Auto In Autos2 If Autos1.Contains(myAuto) Then Autos3.Add(myAuto) End IfNext
Als etwas schönerer generische Funktion:
Textfeld
Private Function Compare2List_Contains(Of mytype)(ByVal lst1 As List(Of mytype), ByVal lst2 As List(Of mytype)) As List(Of mytype) Dim lst3 As New List(Of mytype) For Each Element As mytype In lst2 If lst1.Contains(Element) Then lst3.Add(Element) End If Next Return lst3 End Function
Das funktioniert aber nur unter folgender Voraussetzung:
funktioniert
Dim myAuto As New Auto("BMW", "X3") Autos1.Add(myAuto) Autos2.Add(myAuto)
nicht jedoch, wenn Elemente folgendermaßen hinzugefügt werden:
funktioniert nicht:
Autos1.Add(New Auto("Volkswagen", "Passat")) Autos2.Add(New Auto("Volkswagen", "Passat"))
Das ist jedoch keine zielführende Möglichkeit, wenn man die Listen nach deren Inhalt und nicht nach deren Referenz vergleichen möchte.
Vergleich mit .GetHashCode
Eine andere Methode, wäre über den Vergleich der Elemente mittels .GetHashCode. Dise funktioniert ebenfalls nur bei identischen Referenzen. Als generische Funktion, würde das dann beispielsweise so aussehen:
Compare2List_GetHashCode
Private Function Compare2List_GetHashCode(Of myType)(ByVal lst1 As List(Of myType), ByVal lst2 As List(Of myType)) As List(Of myType) Dim lst3 As New List(Of myType) For Each Element As myType In lst1 Dim tmpElement As myType = Element If lst2.Exists(Function(x As myType) x.GetHashCode = tmpElement.GetHashCode) Then lst3.Add(Element) End If Next Return lst3 End Function
Vergleich der Inhalte
Einen etwas anderen Weg muss man beschreiten, wenn man die Inhalte vergleichen möchte und nicht nur die Referenz. Eine mit Contains vergleichbare Funktion gibt es nicht.
Vergleich mit .Exists
Das Grundkonzept funktioniert folgendermaßen:
Grundkonzept
Dim Autos3 As New List(Of Auto) For Each myAuto2 As Auto In Autos2 Dim myAuto2tmp As Auto = myAuto2 If Not Autos1.Exists(Function(x As Auto) x.Marke = myAuto2tmp.Marke And x.Modell = myAuto2tmp.Modell) Then Autos3.Add(myAuto2) End IfNext
Will man das in eine Funktion packen, steht man jedoch vor dem Problem, dass man nicht weiß welche Properties der Klasse verglichen werden sollen. In diesem Fall muss man die Klasse um eine Overrides Function erweitern, die beispielsweise .toString oder .GetHashCode ersetzt.
Die Klasse Auto, erweitert um ToString als Overrides Function müsste folgendermaßen aussehen:
überarbeitete Klasse
Public Class Auto Private _marke As String = String.Empty Private _modell As String = String.Empty Public Sub New(ByVal marke As String, ByVal modell As String) Me._marke = marke Me._modell = modell End Sub Public Property Marke() As String Get Return _marke End Get Set(ByVal value As String) Me._marke = value End Set End Property Public Property Modell() As String Get Return _modell End Get Set(ByVal value As String) Me._modell = value End Set End Property Public Overrides Function ToString() As String Return LCase(Me._marke) & "|" & LCase(Me._modell) End FunctionEnd Class
Dann könnte man beispielsweise folgende generische Funktion zum Vergleich verwenden.
Compare2List_Exists
Private Function Compare2List_Exists(Of myType)(ByVal lst1 As List(Of mytype), ByVal lst2 As List(Of mytype)) As List(Of mytype) Dim lst3 As New List(Of myType) For Each Element As myType In lst1 Dim tmpElement As myType = Element If lst2.Exists(Function(x As myType) x.ToString = tmpElement.ToString) Then lst3.Add(Element) End If Next Return lst3 End Function
Ausgangslisten bereinigen
Spinnen wir den Gedanken noch ein Stück weiter. Die 2 Listen sollen nun nicht mehr nur auf in beiden vorkommenden Elemente untersucht werden, sondern es sollen auch in beiden Listen, die Elemente, die in beiden Listen vorkommen, entfernt werden.
Geht man davon aus, dass in Liste1 alle Elemente nur einmal vorkommen, in Liste2 es jedoch auch möglich sein kann, dass Elemente mehrfach vorkommen, könnte man die Listen folgendermaßen bereinigen:
Compare2List_andRemove
Private Function Compare2List_andRemove(Of myType)(ByRef lst1 As List(Of myType), ByRef lst2 As List(Of myType)) As List(Of myType) Dim lst3 As New List(Of myType) Dim i As Integer = 0 Dim foundItems As New List(Of myType) Do Dim tmpElement1 As myType = lst1(i) foundItems = lst2.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString) If foundItems.Count <> 0 Then lst3.Add(tmpElement1) lst1.RemoveAt(i) For Each founditem As myType In foundItems lst2.Remove(founditem) Next foundItems.Clear() Else i = i + 1 End If Loop Until (i = (lst1.Count)) Return lst3 End Function
Wenn man davon ausgeht, dass sowohl Liste1 als auch Liste2 Elemente mehrfach enthält, dann könnte man folgendermaßen vorgehen:
Compare2List_andRemove
Private Function Compare2List_andRemove(Of myType)(ByRef lst1 As List(Of myType), ByRef lst2 As List(Of myType)) As List(Of myType) Dim lst3 As New List(Of myType) Dim i As Integer = 0 Dim foundItems As New List(Of myType) For Each element As myType In lst1 Dim tmpElement1 As myType = element foundItems = lst2.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString) If foundItems.Count <> 0 Then lst3.Add(tmpElement1) For Each founditem As myType In foundItems lst2.Remove(founditem) Next foundItems.Clear() End If Next For Each element As myType In lst3 Dim tmpElement1 As myType = element foundItems = lst1.FindAll(Function(x As myType) x.ToString = tmpElement1.ToString) If foundItems.Count <> 0 Then For Each founditem As myType In foundItems lst1.Remove(founditem) Next foundItems.Clear() End If Next Return lst3 End Function
Dublikate aus Liste entfernen
Selbiges könnte man auch auf eine einzelne List(of T) anwenden, um die Dublikate der Liste zu entfernen.
Liste bereinigen
Private Sub ListeBereinigen(Of myType)(ByRef lst As List(Of myType)) Dim Lst_of_Dublicates As New List(Of myType) For Each element As myType In lst Dim tmpElement As myType = element Dim foundItems As List(Of myType) = lst.FindAll(Function(x As myType) x.ToString = tmpElement.ToString) If foundItems.Count > 1 Then Lst_of_Dublicates.Add(tmpElement) End If Next For Each element As myType In Lst_of_Dublicates Dim tmpElement As myType = element Dim foundItems As List(Of myType) = lst.FindAll(Function(x As myType) x.ToString = tmpElement.ToString) If foundItems.Count <> 0 Then Dim i As Integer = 0 For Each founditem As myType In foundItems If i <> 0 Then lst.Remove(founditem) End If i = i + 1 Next End If NextEnd Sub
Download
Um die Wirkungsweise der Funktionen zu veranschaulichen, stelle ich den SourceCode und das Programm hier zum Download zur Verfügung. (siehe Anhänge)