Home > Artikel > Ausgabe 1/2013 > Programmieren mit Arrays

Programmieren mit Arrays

  PDF ansehen

  Download PDF und Beispieldatenbank

Dass Sie unter Access Daten in Tabellen speichern und gezielt darauf zugreifen können, wissen Sie als Access [basics]-Leser schon längst. Aber was, wenn Sie nur ein paar gleichartige Daten zwischenspeichern und wieder darauf zugreifen möchten? Legen Sie dann im VBA-Code für jedes Element eine eigene Variable an? Nein! Hier kommen zum Beispiel die Arrays, auch Datenfelder genannt, zum Einsatz. Der vorliegende Artikel zeigt, wie Sie Array erstellen, mit Daten füllen und die Daten wieder auslesen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1301_Arrays.mdb.

Arrays

Array sind prinzipiell eine Art Tabelle, wobei Sie nicht über Datensätze und Feldnamen auf den in einem Elemend des Array gespeicherten Wert zugreifen, sondern über Index-Werte.

Zusätzlich kann ein Array eine, zwei oder auch mehr Dimensionen enthalten und diese mit Elementen füllen.

Ein Array mit einer Dimension würde beispielsweise einer Tabelle mit einem einzigen Feld entsprechen und ein Array mit zwei Dimensionen einer Tabelle mit einem bis n Feldern. Arrays mit mehr als zwei Dimensionen lassen sich schlecht auf Tabellen übertragen.

Eindimensionale Arrays

Mit einem eindimensionalen Array lassen sich einfache Wertereihen speichern – beispielsweise die Monatsnamen. Um die Vereinfachung durch den Einsatz von Array zu verdeutlichen, schauen wir uns zunächst an, wie Sie Monatsnamen auf umständliche Weise in Variablen speichern können. Dazu legen Sie in einem Modul namens mdlArrays (VBA-Editor mit Alt + F11 ) öffnen, dann Einfügen|Modul auswählen) zunächst die öffentlich deklarierten String-Variablen an (dieses und die folgenden Beispiele finden Sie im Modul mdlArrays):

Public strMonat01 As String

Public strMonat02 As String

Public strMonat03 As String

...

Public strMonat12 As String

In einer Prozedur namens MonateInVariablen füllen Sie diese mit den entsprechenden Werten:

Public Sub MonateInVariablen()

     strMonat01 = "Januar"

     strMonat02 = "Februar"

     strMonat03 = "März"

     ...

     strMonat12 = "Dezember"

End Sub

Führen Sie diese Prozedur aus, indem Sie die Einfügemarke in der Prozedur platzieren und auf F5 drücken. Danach können Sie die Werte der einzelnen Variablen etwa im Direktfenster (Strg + G) wie folgt abfragen:

Debug.Print strMonat03

März

Wenn Sie per VBA auf eine der Variablen zugreifen möchten, um den Namen des Monats zu ermitteln, müssen Sie immer den genauen Variablen verwenden. Es gibt keine Möglichkeit, die zwölf Variablen beispielsweise innerhalb einer Schleife schnell auszugeben.

Dies ist ein tolles Betätigungsfeld für Array. Ein Array, das eine bestimmte Menge Daten gleichen Datentyps aufnehmen soll (dies ist eine Einschränkung, die Sie beachten müssen), deklarieren Sie fast genauso wie eine normale Variable dieses Datentyps. Der einzige Unterschied ist, dass Sie den größten zu erwartenden Wert für den Index hinter dem Variablennamen in Klammern angeben:

Public strMonate(11) As String

Im Gegensatz zu den weiter unten besprochenen dynamischen Arrays handelt es sich hierbei um ein statisches Array.

Sicher werden Sie sich fragen, warum hier die Zahl 11 und nicht die der Anzahl der Monate entsprechende Zahl 12 angegeben wird. Der Grund ist, dass die Indexwerte standardmäßig immer mit 0 beginnen. Das bedeutet, das die Variable strMonate Werte für die Indexwerte 0 bis 11 aufnehmen kann. Der erste Eintrag des Arrays strMonate wird also mit dem Indexwert 0, der zweite mit 1 und so weiter festgelegt:

Public Sub MonateInArray()

     strMonate(0) = "Januar"

     strMonate(1) = "Februar"

     strMonate(2) = "März"

     ...

     strMonate(11) = "Dezember"

End Sub

Dementsprechend liefert etwa die Abfrage des Wertes mit dem Index 3 den vierten Eintrag des Arrays, hier April:

Debug.Print strMonate(3)

April

Nun scheint es unkomfortabel zu sein, nicht die dem Monat entsprechende Zahl für den Zugriff auf das Array zu verwenden. Dies lässt sich jedoch leicht ändern. Wenn Sie nur einen Wert für die Anzahl der Elemente angeben, geht VBA davon aus, dass der Index 0-basiert verwaltet wird. Sie können den Wertebereich für den Indexbereich jedoch detailliert festlegen. In diesem Fall möchten wir gern die Indexwerte 1 bis 12 verwenden. Dazu deklarieren Sie die Array-Variable wie folgt:

Public strMonate(1 To 12) As String

Die Monatsnamen weisen Sie dementsprechend nun mit den Indexwerten 1 bis 12 zu:

Public Sub MonateInArray()

     strMonate(1) = "Januar"

     strMonate(2) = "Februar"

     strMonate(3) = "März"

     ...

     strMonate(12) = "Dezember"

End Sub

Somit liefert das Array nun auch bei der Ausgabe die gewünschten Werte:

Debug.Print strMonate(3)

März

Was aber geschieht, wenn Sie das Array nullbasiert deklarieren, aber dann nicht die Indexwerte 0 bis 11, sondern 1 bis 12 verwenden? Dann tritt der Fehler aus Bild 1 auf.

Versuch, ein Array mit einem zu großen Indexwert zu füllen

Bild 1: Versuch, ein Array mit einem zu großen Indexwert zu füllen

Das Array wurde nur für Indexwerte bis maximal 11 deklariert, also ist der Indexwert 12 nicht zulässig. Das gilt andersherum genauso – wenn Sie also ein Array für die Indexwerte 1 bis 12 deklarieren und das Element mit dem Indexwert 0 füllen wollen, lösen Sie ebenfalls einen Fehler aus.

Alle Werte auslesen

Kommen wir gleich zu einem wesentlichen Vorteil von Arrays gegenüber einzelnen, durchnummerierten Variablen: Sie können diese mit wenigen Zeilen per Code durchlaufen.

Wenn Sie wissen, welche für welche Indexwerte Sie die Elementinhalte ausgeben möchten, benötigen Sie nur die folgende Prozedur dazu:

Public Sub MonateDurchlaufen()

     Dim i As Integer

     Call MonateInArray

     For i = 1 To 12

         Debug.Print strMonate(i)

     Next i

End Sub

Die Prozedur deklariert eine Laufvariable namens i und füllt das Array strMonate() mit der oben erläuterten Prozedur MonateInArray. Danach durchläuft es eine Schleife und füllt dabei die Laufvariable i nacheinander mit den Werten von 1 bis 12. Die einzige Anweisung innerhalb der Schleife gibt den jeweiligen Wert des Arrays strMonate() für den Index i aus.

Eindimensionale Arrays mit For Each durchlaufen

Bei eindimensionalen Arrays können Sie neben der For...Next-Schleife auch die For Each-Schleife zum Durchlaufen des Arrays einsetzen. Sie müssen nur eine Variant-Variable deklarieren, die mit dem aktuellen Wert des Arrays gefüllt wird:

Public Sub MonateDurchlaufenForEach()

     Dim varmonat As Variant

     Call MonateInArray

     For Each varmonat In strMonate

         Debug.Print varmonat

     Next varmonat

End Sub

Der Vorteil ist, dass Sie sich hier um die Werte für die Indexgrenzen keine Sorgen machen müssen.

Standard für die Indexbasis anpassen

Wenn Sie feststellen, dass Sie grundsätzlich mit Arrays arbeiten, deren Index mit 1 beginnt (oder auch einer völlig anderen Zahl), können Sie die Basis des Indexes für Arrays modulweit auf einen anderen Wert einstellen (Beispiele im Modul mdlArraysBase1).

Für den Wert 1 legen Sie im Kopf des Moduls die folgende Anweisung an:

Option Base 1

Wenn Sie nun das Array für die Indexwerte von 1 bis 12 deklarieren, brauchen Sie wiederum nur die Zahl 12 in Klammern anzugeben:

Public strMonate(12) As String

Um zu prüfen, ob der Index von strMonate wirklich bei 1 beginnt und nicht doch bei 0, fügen Sie die folgende Anweisung innerhalb der Prozedur MonateDurchlaufe ein:

strMonate(0) = "test"

Dies liefert wieder den bereits oben beschriebenen Fehler.

Ein Array dynamisch erweitern

Nicht immer wissen Sie bereits beim Deklarieren eines Arrays, wieviele Werte Sie darin speichern möchten. Für diesen Fall hält VBA die Möglichkeit der dynamischen Erweiterung bereit.

Im folgenden Beispiel soll ein Array namens strFirmen mit den Firmennamen aus der Tabelle tblKunden der Beispieldatenbank gefüllt werden. Um später per Direktfenster auf das Array zugreifen zu können, deklarieren wir es wie folgt im Kopf des Moduls mdlDynamischeArrays:

Dim strFirmen() As String

Wie Sie sehen, haben wir hier nicht festgelegt, in welchen Grenzen sich die Indexwerte befinden dürfen.

Danach führen Sie die folgende Prozedur aus:

Public Sub ArrayAusRecordset()

     Dim db As DAO.Database

     Dim rst As DAO.Recordset

     Set db = CurrentDb

     Set rst = db.OpenRecordset("SELECT Firma " _

         & "FROM tblKunden", dbOpenDynaset)

     Do While Not rst.EOF

         ReDim Preserve strFirmen(rst.AbsolutePosition)

         strFirmen(rst.AbsolutePosition) = rst!Firma

         rst.MoveNext

     Loop

End Sub

Diese Prozedur deklariert und füllt zwei Variablen für das aktuelle Database-Objekt und für ein Recordset mit allen Datensätzen der Tabelle tblKunden.

Eine Do While-Schleife durchläuft alle Datensätze der des Recordsets. Dabei geschehen zwei Dinge: Als Erstes stellt die ReDim-Anweisung den größten Wert für die erste Dimension des Arrays strFirmen auf den Wert der Eigenschaft AbsolutePosition des aktuelle Datensatzes des Recordsets ein. AbsolutePosition liefert die Position des Datensatzzeigers von 0 bis zur Anzahl der Datensätze minus eins.

Dadurch wird das Array mit jedem Durchlauf um ein Element erweitert. Das Schlüsselwort Preserve ist unbedingt zu verwenden: Es sorgt dafür, dass das Array bei der Änderung der Elementanzahl nicht geleert wird.

Die zweite Anweisung füllt das Element mit dem durch rst.AbsolutePosition gelieferten Index mit dem Wert des Feldes Firma des aktuellen Datensatzes. Auf diese Weise füllt die Prozedur das Array in diesem Beispiel mit allen 91 Firmennamen der Tabelle tblKunden. Das letzte Element rufen Sie nach dem Ausführen der Prozedur ArrayAusRecordset beispielsweise mit folgender Anweisung im Direktfenster ab:

  strFirmen(90)

Wolski Zajazd

Indexgrenzen dynamischer Arrays ermitteln

Angenommen, Sie haben wie oben angegeben ein Array Element für Element dynamisch erweitert und gefüllt. Wie wollen Sie dieses dann in einer Schleife durchlaufen, ohne die Indexgrenzen genau zu kennen?

Genau für diesen Zweck gibt es zwei VBA-Funktionen. Die erste heißt LBound und liefert den Wert des untersten Indexes, die zweite heißt UBound und liefert den oberen Wert. L steht dabei für Lower, U für Upper.

Das folgende Beispiel zeigt den praktischen Einsatz dieser beiden Funktionen:

Public Sub DynamischesArrayDurchlaufen()

     Dim i As Integer

     Dim intUntereGrenze As Integer

     Dim intObereGrenze As Integer

     Call ArrayAusRecordset

     intUntereGrenze = LBound(strFirmen())

     intObereGrenze = UBound(strFirmen())

     For i = intUntereGrenze To intObereGrenze

         Debug.Print i, strFirmen(i)

     Next i

End Sub

Die Prozedur deklariert zunächst zwei Variablen namens intUntereGrenze und intObereGrenze, welche die Indexgrenzen aufnehmen. Diese werden mit den durch die Funktionen LBound und UBound ermittelten Werten gefüllt. Anschließend durchläuft eine For...Next-Schleife alle Werte von der untersten bis zur obersten Indexgrenze.

Innerhalb der Schleife gibt eine Debug.Print-Anweisung den Wert der Laufvariablen sowie den Wert des Arrays für diesen Indexwert aus.

Es gibt auch noch eine Kurzfassung, welche die Indexgrenzen direkt in der For...Next-Schleife angibt:

Public Sub DynamischesArrayDurchlaufen_Kurz()

     Dim i As Integer

     Call ArrayAusRecordset

     For i = LBound(strFirmen()) To _

             UBound(strFirmen())

         Debug.Print i, strFirmen(i)

     Next i

End Sub

Arrays als Parameter übergeben

Wenn Sie ein Array in einer Funktion zusammenstellen und dieses als Rückgabewert verwenden möchten, muss das Array als Variant-Variable statt etwa als String-Variable deklariert werden. Die Funktion, welche das Variant-Array zusammenstellt, sieht so aus:

Public Function FirmenEinlesen() As Variant

     Dim db As DAO.Database

     Dim rst As DAO.Recordset

     Dim varFirmen() As Variant

     Set db = CurrentDb

     Set rst = db.OpenRecordset("SELECT Firma " _

         & "FROM tblKunden", dbOpenDynaset)

     Do While Not rst.EOF

         ReDim Preserve varFirmen(rst.AbsolutePosition)

         varFirmen(rst.AbsolutePosition) = rst!Firma

         rst.MoveNext

     Loop

     FirmenEinlesen = varFirmen

End Function

Die folgende Prozedur ruft die Funktion FirmenEinlesen auf und schreibt den Rückgabewert in die Variable varFirmen. Die letzte Anweisung gibt testweise ein Element des Arrays aus:

Public Sub ArrayAlsParameter()

     Dim varFirmen() As Variant

     varFirmen = FirmenEinlesen

     Debug.Print varFirmen(90)

End Sub

Wenn Sie – entweder in der aufrufenden Prozedur oder in der Funktion – das Array als String deklarieren, erzeugt dies einen Laufzeitfehler.

Zweidimensionale Arrays

Arrays können auch mehr als eine Dimension umfassen, zum Beispiel zwei. Ein zweidimensionales Array deklarieren Sie ähnlich wie ein eindimensionales Array – mit dem Unterschied, dass Sie statt einer nun zwei Elemente mit Indexbegrenzungen in Klammern hinter dem Namen der Variablen eingeben.

In den folgenden Beispielen kümmern wir uns zunächst um statische Arrays und wollen Monate und deren Abkürzungen im Array speichern. Die erste Dimension benötigt also zwölf Elemente, die zweite derer zwei. Bei einer nullbasierten Notation sieht dies so aus:

Dim strMonate(11, 1) As String

Auch hier können Sie Bereiche angeben, was in diesem Beispiel zumindest für die erste Dimension sinnvoll ist:

Dim strMonate(1 To 12, 0 To 1)

Die Indexgrenzen ermitteln Sie für die verschiedenen Dimensionen, indem Sie als zweiten Parameter die Nummer der Dimension angeben – hier interessanterweise 1-basiert.

Die folgende Anweisung gibt die Ober- und Untergrenze für die erste und zweite Dimension des oben deklarierten Arrays aus, hier also 0, 11, 0, 1:

Debug.Print LBound(strMonate, 1), UBound(strMonate, 1),

LBound(strMonate, 2), UBound(strMonate, 2)

Mehrdimensionales Array füllen

Um ein solches Array zu füllen, weisen Sie der Variablen wieder den entsprechenden Wert zu. Diesmal geben Sie allerdings nicht nur einen, sondern zwei Indexwerte an. Dazu stellen wir uns die erste Dimension als Zeilen und die zweite als Spalten vor.

Die ersten Daten für den Monatsnamen und die Abkürzung weisen Sie dann wie folgt zu:

Public Function MonateEinlesen() As Variant

     Dim strMonate(1 To 12, 0 To 1) As Variant

     strMonate(1, 0) = "Januar"

     strMonate(1, 1) = "Jan"

     strMonate(2, 0) = "Februar"

     strMonate(2, 1) = "Feb"

     ...

     strMonate(12, 0) = "Dezember"

     strMonate(12, 1) = "Dez"

     MonateEinlesen = strMonate

End Function

Mehrdimensionales Array lesen

Wenn Sie alle Elemente des obigen Arrays lesen möchten, brauchen Sie zwei ineinander verschachtelte For...Next-Schleifen:

Public Sub MonateAusgeben()

     Dim strMonate() As Variant

     Dim i As Integer

     Dim j As Integer

     strMonate() = MonateEinlesen

     For i = 1 To 12

         For j = 0 To 1

             Debug.Print strMonate(i, j),

         Next j

         Debug.Print

     Next i

End Sub

Die äußere Schleife durchläuft jede Zeile des Arrays, die innere jeweils die Spalten einer jeden Zeile. Die innerhalb der inneren Schleife Debug.Print-Anweisung gibt also erst den kompletten Monatsnamen, dann die Abkürzung aus – jeweils durch einen Tabulatorschritt voneinander getrennt. Damit die Prozedur nicht alle Elemente in einer Zeile ausgibt, folgt nach jeder Zeile ein einfaches Debug.Print ohne Inhalt.

Maximale Anzahl der Dimensionen

Ein Array kann maximal 64 Dimensionen aufnehmen. In der Regel verwendet man jedoch maximal drei Dimensionen, was praktisch einem Würfel entspricht.

Array leeren

Es gibt zwei Varianten, ein Array zu leeren: entweder Sie deklarieren es neu (und definieren auch die Dimensionen erneut) oder Sie verwenden die Erase-Funktion mit dem Namen des Arrays als Parameter – zum Beispiel so:

Erase strFirmen()

Der Unterschied zeigt sich bei dynamisch gefüllten Arrays: Wenn Sie ein dynamisches Array (also eines ohne feste Angabe der Elementanzahl) neu deklarieren, werden nicht nur die Elemente geleert, sondern auch alle Elemente gelöscht. Die Erase-Anweisung hingegeben leert nur die Elementinhalte.