version=2.12.3
vdate=04.09.2019
fname=iterator.cls
ns=%NAMESPACE%
fpath=/vba/classes
====== [VBA] Iterator ======
//Eine Iterator-Klasse um die verschiedenen Listenmöglichkeiten (Array, Collection, Dictioary etc.) alle gleich handhaben zu können//
==Version %%version%% %%vdate%%==
>//Das Modul hat versteckte Attribute. Damit diese aktiv übernommen werden reicht es nicht aus, den Code in ein neues Modul zu kopieren. Man muss das Modul aus der Datei nach VBA importieren.//
>{{popup>:vba:vba_importfile.png|Bild zum Import}}
{{..%%fname%%|Download %%fname%% (V-%%version%%)}}
In VBA gibt es verschiedene Listen-Arten. Array, Collection und Dictionary. Wenn man noch mit %%RegExp%% arbeitet kommen %%MatchCollection%% und Match.Submatches dazu. Bei Recordset die Fields.
Jeder dieser Listen muss anderst behandelt werden. Mal beginnen die Indexe irgendwo (array), mal bei 1 (Collection), meistens bei 0 oder im Fall von Dictionaries muss man über den Key zugriffen werden.
Ich wollte ein einheitliches Konstrukt, durch dass ich bequem mittels Loop durchiterieren kann. Dabei kam die Iterator-Klasse heraus.
Am besten mal die [[#anwendungsbeispiele|Anwendungsbeispiele]] anschauen um zu verstehen um was es geht.
Die folgenden Listen sind ausprogrammiert:
\\ Array,
Collection(([[http://msdn.microsoft.com/de-de/library/yb7y698k%28v=vs.90%29.aspx|Collection bei MSDN]])),
Dictionary(([[http://msdn.microsoft.com/en-us/library/x4k5wbx4%28v=vs.84%29.aspx|Dictionary bei MSDN]])),
%%RegExp.MatchCollection%%(([[http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.matchcollection%28v=vs.110%29.aspx|MatchCollection bei MSDN]])),
%%RegExp.Submatches%%,
DAO.Recordset,
DAO.Fields
DAO.QueryDef
DAO.TableDef
Excel.Workbook (Die Sheets)
Excel.Sheets
FSO.Folder
===== Definitionen =====
==== Enumerator ====
=== itParams ===
'/**
' * Parameter zur Steuerung des Verhalten des Iterators
' * Die einzelnen Parameter sind mit + kombinierbar
' */
Public Enum itParams
itNone = 0 'Kein Parameter hat gültigkeit
itNothingAsEmptyList = 2 ^ 0 'Leerer String, Nothing, Empty, Null wird als leere Liste akzeptiert und wirft kein Fehler
itListNextNoParamsAsToNext = 2 ^ 1 'Wenn bei listNext keine Parameter angeben werden, einfach eins vorrücken.
itErrorAtEmptyList = 2 ^ 2 'Fehler generieren, wenn die iListe leer ist
itDaoValue = 2 ^ 3 'Field-Value anstelle von Field ausgeben
itIndexInsteadKey = 2 ^ 4 'Gibt bei den toX() Funktionen den Index anstelle des Keys zurück
itDefault = itNothingAsEmptyList + itListNextNoParamsAsToNext
[_LAST] = 4
End Enum
==== Properties ====
Die Eigenschaften der Klasse.
\\ r steht für Readable, w für writable
=== Properties zu den Daten ===
***source**rw Quellliste, über die Iteriert wird (Array, Collection etc.)
***current**r Aktuelles Item
***key**r Aktueller Key
***index**r Aktueller Indes der Liste, beginnend mit dem listenspezifischen Anfang
***absolutePosition**r Absolute Position in der Liste, beginnend mit 0
***BOF**r Beginn of File. Wir sind vor dem ersten Eintrag
***%%EOF%%**r End of File. Wir sind nach dem letzten Eintrag
***count**r Anzahl Items in der Liste
***isEmpty**r Flag, ob überhaubt Einträge vorhanden sind: count=0
=== Propierties zur Iteratorsteuerung ===
Diese Properties sind die Einzeilzuordnungen zu [[##itparams|itparams]]
***Params**rw Die Parameter als Enum-Set
***paramNothingAsEmptyList**rw Leerer String, Nothing, Empty, Null wird als leere Liste akzeptiert und wirft kein Fehler
***paramListNextNoParamsAsToNext**rw Wenn bei listNext keine Parameter angeben werden, einfach eins vorrücken.
***paramErrorAtEmptyList**rw Fehler generieren, wenn die iListe leer ist
***paramDaoValue**rw Field-Value anstelle von Field ausgeben
***paramIndexInsteadKey**rw Gibt bei den toX() Funktionen den Index anstelle des Keys zurück
==== Methoden ====
=== Collection Class Methodes ===
Diese Funktionen dienesn dazu, dass die Klasse wie eine Collection genutzt werden kann.
\\ [[http://msdn.microsoft.com/en-us/library/aa262338%28v=vs.60%29.aspx]]
== NewEnum() ==
'/**
' * Der NewEnum wird für die For Each.. Next Schleife verwendet
' *
' * Diese Funktion hat das Attribut "'Attribute NewEnum.VB_UserMemId = -4"
' * !! Diese Iterierung hat keinen Einfluss auf die aktuelle Position !!
' *
' * @return Das nächste element
' */
Public Function NewEnum() As IUnknown
== item() ==
'/**
' * Entspricht dem item()-Befehl der Listenobjekte
' * Alle 3 Propert-Funktionen aben das versteckte Attribut "Attribute item.VB_UserMemId = 0"
' * Dadurch kann mittels it(1), it("MyFeild"), it!MyField etc darauf zugegriffen werden.
' *
' * !! Das Verhalten dieser Funktion ist NICHT standartisiert. Sie entspricht dem entsprechenden Verhalten der Source !!
' *
' * @param Variant Index/Key
' * @return Variant
' */
Public Property Get item(ByVal iIndex As Variant) As Variant
=== Initialisierungs-Methoden ===
== initialize() ==
'/**
' * Initialisuert den Iterator
' * @param Variant Die Liste, über welche Iteriert werden soll
' * @param itParams Parameters
' * @return Boolean true: iList ist eine Liste und hat Werte
' */
Public Static Function initialize( _
ByRef iList As Variant, _
Optional ByVal iParams As itParams = itDefault _
) As Boolean
== initializeRs() ==
Die Parameters findest du [[#itparams|hier]]. Sie sind mit + kombinierbar.
'/**
' * Analog zu initialze. Erstellt aber direkt ein Recordset aus einem SQL
' * @param String SLQ String
' * @param itParams Parameters
' * @return Boolean true: iList ist eine Liste und hat Werte
' */
Public Static Function initializeRs( _
ByVal iSql As String, _
Optional ByVal iParams As itParams = itDefault _
) As Boolean
=== Navigations-Methoden ===
== toNext() ==
'/**
' * Geht zum nächsten Datensatz. Der Key und er Datensatz werden als Parameter zurückgegeben, EOF als Return-Value
' * @param Variant Key oder pos des Items
' * @param Variant Das Item himself
' * @return Boolean EOF
Public Function toNext( _
Optional ByRef oKey As Variant = Null, _
Optional ByRef oItem As Variant = Null _
) As Boolean
== toPrev() ==
'/**
' * Geht zum nächsten Datensatz. Der Key und er Datensatz werden als Parameter zurückgegeben, EOF als Return-Value
' * @param Variant Key oder pos des Items
' * @param Variant Das Item himself
' * @return Boolean EOF
Public Function toPrev( _
Optional ByRef oKey As Variant = Null, _
Optional ByRef oItem As Variant = Null _
) As Boolean
== toFirst() ==
'/**
' * Geht zum ersten Datensatz.
' * Vorsicht, während reset() eins vor den ersten Datensatz geht, geht toFirst() bereits auf den ersten Datensatz
' * Somit ists das eigentlich reset()+toNext()
' * @param Variant Key oder pos des Items
' * @param Variant Das Item himself
' * @return Boolean EOF
Public Function toFirst( _
Optional ByRef oKey As Variant = Null, _
Optional ByRef oItem As Variant = Null _
) As Boolean
== toLast() ==
'/**
' * Geht zum nächsten Datensatz. Der Key und er Datensatz werden als Parameter zurückgegeben, EOF als Return-Value
' * @param Variant Key oder pos des Items
' * @param Variant Das Item himself
' * @return Boolean EOF
Public Function toLast( _
Optional ByRef oKey As Variant = Null, _
Optional ByRef oItem As Variant = Null _
) As Boolean
== toPosition() ==
'/**
' * Geht zum nächsten Datensatz. Der Key und er Datensatz werden als Parameter zurückgegeben, EOF als Return-Value
' * @param Long Absolute Position. Zwischen 0 und Count-1
' * @param Variant Key oder index des Items
' * @param Variant Das Item himself
' * @return Boolean EOF
Public Function toPosition( _
ByVal iAbsolutPosition As Long, _
Optional ByRef oKey As Variant = Null, _
Optional ByRef oItem As Variant = Null _
) As Boolean
== listNext() ==
Siehe auch [[:vba:functions:list]]
'/**
' * Rückt den Zeiger um Eins vor und gibt die Elemente einer Unterliste aus.
' * @paramArray Variant Auflistung der Variablen, die abgefüllt werden
' * @return EOF
' */
Public Function listNext( _
ParamArray oParams() As Variant _
) As Boolean
== list() ==
Siehe auch [[:vba:functions:list]]
'/**
' * Ggibt die Elemente der aktuellen Unterliste aus.
' * @paramArray Variant Auflistung der Variablen, die abgefüllt werden
' * @return EOF & BOF
' */
Public Function list( _
ParamArray oParams() As Variant _
) As Boolean
== reset() ==
'/**
' * Setzt den pos auf den Start-1 zurück, so dass bei einem toNext() der erste Datensatz kommt
' */
Public Sub reset()
== subIterator() ==
Wenn das Current-Objekt selber wieder iterierbar ist, kann mit dieser Methode direkt ein Iterator für das Objekt erstellt werden.
'/**
' * Gibt current als Iterator zurück
' * @return Iterator
' */
Public Property Get subIterator() As Iterator
===== Iterierbare Listen =====
Die folgenden Datentypen werden vom Iterator als Liste erkannt und können iteriert werden
*Array
*Dictionary
*Collection
*%%RegExp.MatchCollection%%
*%%RegExp.Match%%
*Dao.Recordset
*Dao.Fields
*Dao.Field
*Properties
===== Anwendungsbeispiele =====
Die Beispiele sind unvollständig. Die Klasse hat noch einiges mehr auf Lager. Finde es heraus oder frag nach.
==== Iteration mit einem eindimensionalen Array ====
=== Einfachstes Beispiel mit einem Array ===
Public Sub testIt()
Dim arr() As Variant: arr = Array(1, 2, 3)
Dim it As New Iterator: it.initialize arr
Do While it.toNext
Debug.Print it.key & ": " & it.current
Loop
End Sub
=== Dito, aber der Wert wird als Variable verarbeitet ===
Public Sub testIt()
Dim arr() As Variant: arr = Array(1, 2, 3)
Dim it As New Iterator: it.initialize arr
Dim key As Variant
Dim val As Variant
Do While it.toNext(key, val)
Debug.Print key & ": " & val
Loop
End Sub
=== Von hinten nach vorne iterieren ===
Public Sub testIt()
Dim arr() As Variant: arr = Array(1, 2, 3)
Dim it As New Iterator: it.initialize arr
it.toEOF
Do While it.toPrev
Debug.Print it.KEY & ": " & it.current
Loop
End Sub
=== Und ein Array, der nicht bei 0 beginnt ===
Public Sub testIt()
Dim arr(3 To 4) As Variant
Dim it As New Iterator
Dim key As Variant
Dim val As Variant
arr(3) = 33
arr(4) = 44
it.initialize arr
Do While it.toNext(key, val)
Debug.Print key & ": " & val
Loop
End Sub
==== Iteration über einen 2 Dimensionalen Array ====
=== Mit direktem Auslesend er SubItems mit listNext() ===
Public Sub testIt()
Dim arr(3 To 4) As Variant
Dim it As New Iterator
Dim v1, v2, v3
arr(3) = Array(31, 32, 33)
arr(4) = Array(41, 42, 44)
it.initialize arr
Do While it.listNext(v1, v2, v3)
Debug.Print v1 & "-" & v2 & "-" & v3
Loop
End Sub31-32-33
41-42-44
==== Besipel mit MatchCoellection ====
Public Sub testIt()
Dim rx As New regExp
Dim mc As Variant
Dim it As Iterator
Dim x, y
rx.Global = True
rx.pattern = "([A-H])([1-8])"
Set mc = rx.execute("Springer von A1 auf B3")
Set it = New Iterator: it.initialize mc
Do While it.toNext
Debug.Print it.current.value 'Den gesamten gefunden String ausgeben
it.list x, y 'Die 2 Submatches auf x und y verteilen
Debug.Print x & ":" & y 'x & y ausegeben
Loop
End SubA1
A:1
B3
B:3
==== Beispiele zu Recordset ====
=== Recordset durchiterieren ===
Public Sub testIt()
'Initialiseren des Iterators über initializeRs
Dim it As New Iterator: it.initializeRs "SELECT [id], [number_one], [number_two] FROM [_test]"
'Jede ID ausgeben
Do While it.toNext
Debug.Print it!id
Loop
it.reset 'Postion zurücksetzen
'Die Felder des Recordsets direkt in die Variablen übertragen
Dim id, n1, n2
Do While it.listNext(id, n1, n2)
Debug.Print id, n1, n2
Loop
End Sub 1
2
3
4
1 11 21
2 12 22
3 13 23
4 14 24
=== FieldList durchiterieren - Ausgabe von current und value ===
Public Sub testIt()
Dim v As Variant
'Initialiseren des Iterators über initializeRs
Dim it As New Iterator: it.initializeRs "SELECT [id], [number_one], [number_two] FROM [_test]"
'Erste Zeile als [Fields] auslesen und neuen Iterator daruas erstellen
it.toFirst
Dim itFields As Iterator: Set itFields = it.subIterator
Debug.Print "== Ausgabe: Property current"
Do While itFields.toNext
print_r itFields.current
Loop
Debug.Print "== Ausgabe: Property value"
itFields.reset 'position zurücksetzen
Do While itFields.toNext
print_r itFields.value
Loop
Debug.Print "== Ausgabe: Referenziertes Property current, Iterator mit dem Parameter DaoValue"
itFields.reset 'position zurücksetzen
itFields.paramDaoValue = True 'Paraeter DaoValue setzen um das Verhalten von current zu ändern
Do While itFields.toNext(, v) 'Der zweite Parameter von toNext() entspricht dem current
print_r v
Loop
End Sub== Ausgabe Property current
(
[OrdinalPosition] => 0
[name] => 'id'
[value] => 1
[type] => 4
)
(
[OrdinalPosition] => 1
[name] => 'number_one'
[value] => 11
[type] => 4
)
(
[OrdinalPosition] => 2
[name] => 'number_two'
[value] => 21
[type] => 4
)
== Ausgabe Property value
1
11
21
== Ausgabe Property current, Iterator mit dem Parameter DaoValue
1
11
21
===== Code =====