|
Sie möchten doppelte Elemente aus einer ListBox oder aus einer ComboBox entfernen? Bei einer sortierten ListBox (alles weitere gilt auch gleichermaßen für eine ComboBox) ist das relativ einfach. Sie durchlaufen alle Elemente und entfernen diejenigen Elemente, die gleich dem vorhergehenden sind:
Dim l As Long
Dim nListCount As Long
Dim nLastItem As String
Dim nThisItem As String
With ListBox
nListCount = .ListCount
Do
nThisItem = .List(l)
If nThisItem = nLastItem Then
.RemoveItem l
nListCount = nListCount - 1
Else
nLastItem = nThisItem
l = l + 1
End If
Loop Until l = nListCount
End With
Bei einer unsortierten ListBox geht es nicht so einfach. Theoretisch müssten Sie nämlich jedes Element mit jedem vergleichen. Sie können zwar trickreich schon verglichene Elemente aus darauf folgenden Vergleichen ausnehmen, aber trotzdem dauert das um so länger, je mehr Elemente die ListBox enthält.
Es müsste also eine Möglichkeit geben, sich jedes bereits geprüfte Element zu merken und nur noch zu prüfen, ob sich ein nachfolgendes Element bereits unter den geprüften Elementen befindet. An sich ist das auch kein Problem - die bereits geprüften Elemente werden in einem Array abgelegt. Jedes nachfolgende Element wird nun nur noch mit den in diesem Array vorhandenen Elementen verglichen. Aber auch hier steigt der Aufwand mit zunehmender Anzahl der Elemente. So ganz überzeugend ist dieses Verfahren also auch noch nicht.
Eine Variante wäre, in diesem Array nicht mehr die geprüften Elemente abzulegen, sondern nur noch jeweils einen eindeutigen Schlüssel, der sich über ein Element errechnen lässt, etwa einen Hashcode in einer Hash-Tabelle. Zwar muss dieser Schlüssel für jedes nachfolgende Element neu berechnet werden, damit er mit den bereits abgelegten Schlüsseln verglichen werden kann, aber das dürfte unterm Strich wahrscheinlich schneller sein, als String-Vergleiche.
Nach diesem Prinzip arbeitet das Verfahren, das ich Ihnen als Lösung vorstelle. Es verwendet eine Collection, die intern bereits eine Hash-Tabelle (oder ein ähnlich schnelles Prinzip) zur Verwaltung der als String übergebenen Schlüssel verwendet. Sie brauchen also in diese Collection nur ein "Nichts" (Empty) einzufügen und dabei den Element-String selbst als Schlüssel angeben. Die Prüfung, ob ein nachfolgendes Element bereits in der Collection enthalten ist, könnte nach dem Versuch-Irrtum-Prinzip geschehen: Sie versuchen, ein neues "Nichts" unter dem Element als Schlüssel einzufügen. Schlägt der Versuch fehl, ist das Element bereits in der Liste vorhanden und kann somit entfernt werden.
Dim nItems As Collection
Dim l As Long
Dim nListCount As Long
With ListBox
nListCount = .ListCount
Set nItems = New Collection
On Error Resume Next
Do
nItems.Add Empty, .List(l)
If Err.Number Then
.RemoveItem l
nListCount = nListCount - 1
Err.Clear
Else
l = l + 1
End If
Loop Until l = nListCount
End If
End With
Aber auch dies lässt sich noch optimieren. Mit dem Ansatz des Versuches, den gleichen Schlüssel beim Einfügen mehrfach zu verwenden, habe ich Sie nämlich aufs Glatteis geführt. Für das Einfügen, auch wenn es fehlschlägt, braucht Visual Basic nämlich relativ viel Zeit. Fast doppelt so schnell ist der Versuch, ein Collection-Element zu einem gegebenen Schlüssel auszulesen, und nur beim Fehlschlagen dieses Versuchs ein Element mit dem sich nun als noch unbekannt herausstellenden Schlüssel in die Collection einzufügen. Packen wir dieses Verfahren gleich in eine praktische Hilfsfunktion:
Public Sub UniqueItemsLB(ListBox As ListBox)
Dim nItems As Collection
Dim l As Long
Dim nListCount As Long
Dim nThisItem As String
Dim nVar As Variant
With ListBox
nListCount = .ListCount
Set nItems = New Collection
On Error Resume Next
Do
nThisItem = .List(l)
nVar = nItems(nThisItem)
If Err.Number Then
nItems.Add Empty, nThisItem
l = l + 1
Else
.RemoveItem l
nListCount = nListCount - 1
Err.Clear
End If
Loop Until l = nListCount
End With
End Sub
Bei einer unsortierten ListBox ist dieses Verfahren um ein Mehrfaches schneller, als die ursprünglich angedachte Lösung, jedes Element mit jedem zu vergleichen. Und bei einer sortierten ListBox ist es immerhin noch weit mehr als doppelt so schnell, wie unser bereits zu Anfang vorgestelltes und bereits sehr effizient erscheinendes Verfahren. Genau genommen ist es diesem letzten Verfahren nun völlig egal, ob es sich um eine sortierte oder um eine unsortierte ListBox handelt - es ist also noch nicht einmal eine optimierende Unterscheidung in dieser Hinsicht notwendig.
|