Suggesties | SortedList |
1. Wat is een SortedList ? 2. Waarvoor een SortedList ? 3. Achtergrond van de SortedList 4. Maak een SortedList 4.1 Early binding 4.1.1 Local scope 4.1.2 Private scope 4.1.3 Public scope 4.2 Late binding 4.2.1 Local scope 4.2.2 Private scope 4.2.3 Public scope 5. Vul een SortedList 5.1 items toevoegen 5.1.1 Methode .Add 5.1.2 Methode .Item 5.3 Soorten Items 5.3.1 lege tekstreeks 5.3.2 gewone tekstreeks 5.3.3 niet-afdrukbaar teken 5.3.4 getal 5.3.5 datum 5.3.6 1-dimensionele array 5.3.7 meer-dimensionele array 5.3.8 object 5.3.9 controls in Userform 5.3.10 ActiveX controls 5.3.11 alle werkbladen 6. De omvang van een SortedList 7. Controle of een item voorkomt 7.1 Controle op sleutel 7.2 Controle op waarde 8.1 De index van een sleutel 8.2 De index van een waarde 9. Lezen van elementen 9.1 1 element met index 9.2 1 element met sleutel 9.3 1 element met sleutel-index < 9.4 alle waarden op index 9.5 alle sleutels op index 10 Wijzig een element 11 Sorteren van elementen 11.1 Aflopend sorteren 12 Verwijder elementen 12.1 1 element op sleutel 12.2 1 element op index 12.3 1 element op inhoud 12.4 alle elementen 13. Kopie van een SortedList 14. Trefwoorden |
Een SortedList in VBA is een verzamelobject: je kunt er allerlei verschillende zaken in opbergen: getallen, teksten, datums, arrays, ranges, variabelen en objecten. VBA heeft verschillende andere mogelijkheden om gegevens op te slaan: - een dictionary - een collection - een array variabele - een ActiveX ComboBox - een ActiveX ListBox - een Userform ComboBox - een Userform ListBox - een Arraylist De keuze voor een van deze methoden is afhankelijk van het te bereiken doel. In deze pagina wordt geen poging gedaan al deze methoden met elkaar te vergelijken. We beperken ons tot een bespreking van de mogelijkheden van de SortedList. Geen van de andere 'verzamel' methoden zoals Collection, Array of Dictionary heeft een sorteermethode. De sorteermethode van de SortedList is (naast die van de 'Arraylist') de enige mij bekende VBA-sorteermethode. Deze sorteermethode is erg snel. Dat kan een argument zijn om juist van een SortedList gebruik te maken. Een ander kenmerk van de Sortedlist is, dat ieder element een sleutel heeft. De Arraylist sorteert op de inhoud van de elementen, de Sortedlist sorteert op sleutel.
In plaats van gegevens te bewerken in een Excel-werkblad, een Word Document, een Powerpointpresentatie, doe je dat in het werkgeheugen. Daarvoor hoeft bijv. geen scherm ververst te worden, berekeningen uitgevoerd te worden, waardoor de SortedList snel is.
De SortedList is onderdeel van de bibliotheek System.Collections. Die bevindt zich in het bestand ....\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb of in een vergelijkbare directory Je kunt via de VBEditor een aktieve verbinding leggen naar dit bestand door mscorlib.dll bij de Referenties aan te vinken (Extra/referenties... of Tools/References..) Als je een bestand waarin je gebruik maakt van een SortedList verspreidt, wordt meteen ook die verbinding naar deze bibliotheek meeverspreid. In VBA kun je op twee verschillende manieren die referentie maken ThisWorkbook.VBProject.References.AddFromFile "C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb" ThisWorkbook.VBProject.References.AddFromguid "{BED7F4EA-1A96-11D2-8F08-00A0C9A6186D}", 2, 4 Je kunt de code ook pas een verbinding laten leggen op het moment dat je de SortedList nodig hebt ('late binding'). Daarvoor gebruik je dan deze VBA code: createobject("System.Collections.SortedList")
Dat is afhankelijk van: - de manier waarop je naar de bibliotheek van de SortedList wil verwijzen: 'early binding' of 'late binding' - de 'scope' van de SortedList: wil je hem in 1 procedure of meer procedures gebruiken:'local scope','private scope','public scope'
zonder toewijzing aan een variabele: With new SortedList .Add "aa1"
end withdim a_00 as New SortedList a_00.Add "aa1" set a_00 = New SortedList a_00.add "aa1" dim a_00 as Object set a_00= new SortedList a_00.add "aa1" dim a_00 as SortedList set a_00= new SortedList a_00.add "aa1" Dan dien je altijd met een variabele te werken. Die variabele moet je eerst in het declaratiegebied van de codemodule declareren. Je kunt het gegevenstype van de variabele voor een SortedList declareren als Object, SortedList of Variant. Omdat de Variant het default gegevenstype is, kun je de specificatie van dit gegevenstype ook achterwege laten. In het declaratiedeel van de codemodule zet je Dim a_00 Private a_00 Dim a_00 as Variant Private a_00 as Variant Dim a_00 as Object Private a_00 as Object Dim a_00 as SortedList Private a_00 as SortedList Set a_00 = New SortedList Dim a_00 As New SortedList Private a_00 as New SortedList Als je een SortedList toegankelijk wil maken voor alle procedures (macro's of funkties) in het werkboek, Userform, Macromodule of Classmodule maak je gebruik van Public Scope. Dan dien je altijd met een variabele te werken.
Vervolgens moet je aan de variabele een nieuwe instantie van de SortedList toewijzen. Wanneer je gebruik maakt van 'early binding' kan dat heel compact in één keer met deze code in het declaratiegebied. Public a_00 As New SortedList
Je kunt het gegevenstype van de variabele voor een SortedList declareren als Object, SortedList of Variant. Omdat de Variant het default gegevenstype is, kun je de specificatie van dit gegevenstype ook achterwege laten. In het declaratiegebied Public a_00 Public a_00 as Variant Public a_00 as Object Public a_00 as SortedList Wijs een nieuwe instantie van de SortedList toe aan de gedeclareerde variabele. set a_00 = new SortedList
zonder toewijzing aan een variabele: With CreateObject("System.Collections.SortedList") .Add "aa1"
end withset a_00 = CreateObject("System.Collections.SortedList") a_00.Add "aa1" Dan dien je altijd met een variabele te werken. Die variabele moet je eerst in het declaratiegebied van de codemodule declareren. Je kunt het gegevenstype van de variabele voor een SortedList declareren als Object of Variant. Omdat de Variant het default gegevenstype is, kun je de specificatie van dit gegevenstype ook achterwege laten. Vervolgens wijs je in een procedure (macro of funktie) een nieuwe instantie van de SortedList toe aan de gedeclareerde variabele. In het declaratiedeel van de codemodule Dim a_00 Private a_00 Dim a_00 as Variant Private a_00 as Variant Dim a_00 as Object Private a_00 as Object Set a_00 = CreateObject("System.Collections.SortedList") Als je een SortedList toegankelijk wil maken voor alle procedures (macro's of funkties) in het werkboek, Userform, Macromodule of Classmodule maak je gebruik van Public Scope. Dan dien je altijd met een variabele te werken. De declaratie van die variabele dient te staan in het declaratiegebied van een macromodule (dus niet in de codemodule van een userform, werkblad, het werkboek of een klassemodule). In welke macromodule de declaratie staat maakt niet uit. Het gegevenstype voor de SortedlList kan zijn Variant of Object. Omdat er nog geen verbinding is gelegd met de SortedList-bibliotheek kun je het gegevenstype 'SortedList' niet gebruiken. In een procedure (macro of funktie) wijs je een nieuwe instantie van de SortedList toe aan de gedeclareerde variabele. In het declaratiedeel van de codemodule van een macromodule Public a_00 Public a_00 as Variant Public a_00 as Object set a_00 = CreateObject("System.Collections.SortedList") Voor het vervolg in deze pagina zal ik voor de overzichtelijkheid alleen gebruikmaken van de With ... End With methode in een 'late binding' situatie. Ieder element in een sortedlist heeft een sleutel (key) en een waarde. Een sleutel is uniek. De elementen in de SortedList worden op hun sleutel oplopend gesorteerd. Er zijn 2 methodes om elementen aan een SortedList toe te voegen: .Add en .Item("sleutel"). De methode .Add genereert een foutmelding als de sleutel al bestaat. De methode .Item("sleutel") vervangt het aan die sleutel gekoppelde element. De inhoud van een item kan van alles zijn: getallen, teksten, datums, arrays, ranges, variabelen, collections, dictionaries, een lege tekenreeks, niets en objecten Je kunt aan een SortedList alleen afzonderlijke items toevoegen (in tegenstelling tot de ArrayList). Het eerste argument van de method .Add bevat de sleutel, het tweede argument het element zelf. De methode .Add plaatst een item in de SortedList en sorteert het element op basis van de sleutel. De sortering heeft gevolgen voor het indexnummer van het element, niet voor de sleutel of de inhoud. With CreateObject("System.Collections.SortedList") .Add "eerste","inhoud"
.Add "1", New Collection
End With.Add "2",123 .Add "sleutel", Date .Add "laatste", Array("rood", "wit", "groen") De methode .Item plaatst een element in de SortedList en sorteert het nieuwe element op de sleutel. De sortering heeft gevolgen voor het indexnummer van het element, niet voor de sleutel of de inhoud. With CreateObject("System.Collections.SortedList") .Item("aa1")= 1234
End With.Item("aa2")= "tekst" .Item("aa4")= new Collection .Item("aa3")= Array("rood", "wit", "groen"
De inhoud van een item kan van alles zijn: getallen, teksten, datums, arrays, ranges, variabelen, collections, dictionaries, een lege tekenreeks, niets en objecten Hieronder een aantal voorbeelden van elementen met verschillende soorten inhoud. With CreateObject("System.Collections.SortedList")
.Add "c_00" ,vbNullString
End With.Add "c_01","" With CreateObject("System.Collections.SortedList")
.Add "d_00", "abcde"
End With.Item("d_01")="fghi"
With CreateObject("System.Collections.SortedList")
.Add "e_00", vbTab
End With.Item("e_01 ")=vbLf With CreateObject("System.Collections.SortedList")
.Add "f_00", 12345
End With.Item("f_01")= RGB(23, 45, 678) ' typename: Integer
' typename: Long
With CreateObject("System.Collections.SortedList")
.Add "g_00", Date
End With.Add "g_01", CDate("23-04-2012") .Item ("g_00")= DateSerial(2013, 10, 12)
With CreateObject("System.Collections.SortedList")
.Add "h_00", Array("aa1", "aa2", "aa3")
End With.Add "h_01", Split("bb1_cc1_dd1", "_") .Item("h_02")=Array("aa1", "aa2", "aa3")
With CreateObject("System.Collections.SortedList")
ReDim sn(6, 10)
End With.Add "i_00", sn .Item("i_01") = sn With CreateObject("System.Collections.SortedList")
.Add "j_00", Range("A1:K10")
End With.Item("j_01") = Range("A1:K10")
With CreateObject("System.Collections.SortedList")
For Each it In Controls
End With.Add it.name, it
Next
With CreateObject("System.Collections.SortedList")
For Each it In Sheets("sheet1").OLEObjects
End With.Add it.name, it
Next
With CreateObject("System.Collections.SortedList")
For Each sh In Sheets
End With.Add sh.name, sh
Next.Item(.count)= sh
De eigenschap .Count geeft natuurlijk ook meteen het aantal sleutels in de SortedList weer. Voor de eigenschap .count heeft de SortedList ook nog twee equivalenten: .Values.Count en .Keys.Count. With CreateObject("System.Collections.SortedList")
For Each sh In Sheets
End With.Add sh.name, sh
Next.Item(.count) = sh msgbox .Count msgbox .Values.Count msgbox .Keys.Count
With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
End WithIf Not .contains(it) Then .Add it, 1200
NextMsgBox .Contains("aa4") MsgBox .Contains("aa9")
Daarmee kun je voorkomen dat een bestaand item met een sleutel wordt overschreven, of dat een foutmelding wordt gegenereerd. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
End WithIf Not .ContainsKey(it) Then .Add it, 388
Next
Daarmee kun je voorkomen dat een bepaalde waarde aan twee verschillende sleutels wordt toegewezen. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa1", "aa3", "aa4", "aa2")
End WithIf Not .ContainsValue(it) Then .Add .count, it
Next
De eerste sleutel heeft indexnummer 0. Als een sleutel niet bestaat geeft deze methode -1 als resultaat. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
End WithIf Not .contains(it) Then .Add it, 2*.count
Nextmsgbox .IndexOfKey("aa4")
Het eerste element heeft indexnummer 0. Als een waarde niet bestaat geeft deze methode -1 als resultaat. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
End WithIf Not .contains(it) Then .Add it, 2*.count
Nextmsgbox .IndexOfValue(6)
Houd er rekening mee, dat het eerste item in een SortedList indexnummer 0 heeft. Er zijn 2 methoden om dit te doen: .GetByIndex en .GetValueList With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa5", "aa4", "aa6")
End With.Add it, 3*.count
Nextmsgbox .GetByIndex(0) msgbox .GetByIndex(.count-1) msgbox .GetByIndex(3) msgbox .GetValueList(0) msgbox .GetValueList(.count-1) msgbox .GetValueList(3) ' het eerste item
' het laatste item ' het 4e element in de SortedList
With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa5", "aa4", "aa6")
End With.Add it, 3*.count
Nextmsgbox .Item("aa4")
De methode .GetKey en .Getkeylist zijn daarvoor geschikt. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa5", "aa4", "aa6")
End With.Add it, 6*.count
NextMsgbox .getkey(2) MsgBox .getkeylist()(2) In de SortedList zul je alle elementen in een lus af moeten lopen. De methoden .GetByIndex en .GetValueList zijn daarvoor geschikt. Het resultaat is een lijst met waarden, gesorteerd op basis van hun sleutels. De omgekeerde sorteervolgorde krijg je met een aflopende lus. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa5", "aa4", "aa6")
End With.Add it, 6*.count
NextFor j = 0 To .Count - 1 c00 = c00 & .GetByIndex(j)
Nextc01 = c01 & .GetValueList()(j) c02 = c02 & .GetByIndex(.count-1-j) c03 = c03 & .GetValueList()(.count-1-j) Msgbox c00 Msgbox c01 Msgbox c02 Msgbox c03
In de SortedList zul je alle sleutels in een lus af moeten lopen. De methoden .GetKey en .GetKeyList zijn daarvoor geschikt. Het resultaat is een lijst met gesorteerde sleutels. De omgekeerde sorteervolgorde krijg je met een aflopende lus. With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa3", "aa2", "aa6")
End With.Add .count, it
Nextfor j = 0 to .count-1
msgbox c00c00 = c00 & vblf & .getkey(j)
nextc01 = c01 & vbLf & .GetKeylist()(j) c02 = c02 & .GetKey(.count-1-j) c03 = c03 & .GetKeyList()(.count-1-j) msgbox c01 Msgbox c02 Msgbox c03
Het te wijzigen item kun je met de sleutel of met de index benaderen. Als de sleutel bekend is kun je de methode .Item gebruiken. Als de index bekend is is de methode .SetByIndex aangewezen. Met de sleutel: With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa8", "aa12", "aa6")
End With.Add it, it
Nextmsgbox .Item("aa3") .Item("aa3")=1235 msgbox .Item("aa3") With CreateObject("System.Collections.SortedList") For Each it In Array("aa1", "aa2", "aa3", "aa8", "aa12", "aa6")
End With.Add it
Nextmsgbox .GetByIndex(3) .SetByIndex(3)="bb20" msgbox .GetByIndex(3) De SortedList evalueert zelf of er sprake moet zijn van een numerieke sortering of een tekstsortering. Een mengeling van gegevenstypen van sleutels leidt tot een foutmelding. tekstsortering With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa13", "aa22", "aa6")
End With.Add it, .count
Nextfor j = 0 to .count-1 c00 = c00 & vblf & .getkey(j)
nextmsgbox c00 With CreateObject("System.Collections.SortedList")
For Each it In Array(12, 112, 2, 34, 305, 302)
End With.Add .count, it
NextFor j = 0 to .count-1 c00 = c00 & vblf & .getkey(j)
nextmsgbox c00 Wil je de items aflopend terugkrijgen dan moet je ze aflopend opvragen. With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa13", "aa22", "aa6")
End With.Add it, .count
Nextfor j = .count-1 to 0 step -1 c00 = c00 & vblf & .GetByIndex(j)
nextmsgbox c00 With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa13", "aa12", "aa6")
End With.Add it, .count
NextFor j = .count-1 to 0 step -1 c00 = c00 & vblf & .GetKey(j)
nextmsgbox c00
In het argument geef je de naam van de sleutel op. Als het element met de opgegeven inhoud niet bestaat negeert de methode de opdracht; VBA genereert geen foutmelding. Verwijder het eerste item met de waarde "aa3": With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa8", "aa3", "aa12", "aa6")
End With.Add it, .count*5
NextMsgBox .Count .Remove "aa3" MsgBox .Count
Als het opgeven indexnummer niet bestaat genereert VBA een foutmelding. Verwijder het 5e element (met indexnummer 4) With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa4", "aa5", "aa6")
End With.Add it
NextMsgBox .Count .RemoveAt 4 MsgBox .Count
Door op basis van de inhoud naar de bijbehorende index of de sleutel te zoeken kun je met .Remove of .RemoveAt het gewenste element verwijderen. Verwijder op index With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa8", "aa3", "aa12", "aa6")
End With.Add it, .Count * 5
NextMsgBox .Count For j = 0 To .Count - 1 If .getbyIndex(j) = 25 Then
Next.RemoveAt j
End IfExit For MsgBox .Count With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa8", "aa3", "aa12", "aa6")
.
End WithAdd it, .Count * 5
NextMsgBox .Count For j = 0 To .Count - 1 If .getbyIndex(j) = 25 Then
Next.Remove .getkey(j)
End IfExit For MsgBox .Count
With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa8", "aa12", "aa6")
End With.Add it, .count*3
Next.Clear MsgBox .Count
Wijzigingen in die kopie hebben geen effekt op de oorspronkelijke SortedList. Omdat de kopie ook weer een SortedList is, dus een object, is de instructie 'Set' noodzakelijk bij de toewijzing aan een variabele. With CreateObject("System.Collections.SortedList")
For Each it In Array("aa1", "aa2", "aa3", "aa8", "aa12", "aa6")
End With.Add it, it
Nextset c_00=.Clone msgbox c_00.Count c_00.item(2)="~" MsgBox .Item("aa2") & vbTab & c_00.Item("aa2")) |