配列変数

Excelはワークシート自身が「2次元配列変数」、ワークブックは「3次元(的?)配列変数」です。
これをマクロ内部で実現するのが配列変数です。
配列とは
配列とは、他に「テーブル」と呼ぶこともあります。1つの変数名にインデックスを付けておき、そのインデックスの値によって複数の内容を登録したり、呼び出したりする変数です。


'***************************************************************************************************
'   配列処理のサンプル                                              Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'03/06/29(1.00)新規作成
'16/11/19(1.10)*.xlsm化
'20/02/11(1.11)コード整理等
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ シート上のボタン起動処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST3
'* 機能  :配列処理のサンプル
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2003年06月29日
'* 作成者 :井上 治
'* 更新日 :2016年11月19日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub TEST3()
    '-----------------------------------------------------------------------------------------------
    Dim V(10) As Long                           ' Long型の配列(11要素)
    Dim W(1 To 10) As Long                      ' Long型の配列(10要素)
    Dim X() As Long                             ' Long型の配列(要素数不定)
    Dim Y(1 To 10, 1 To 10) As Long             ' Long型の配列(10×10要素)
    ' 不定要素数だった変数を11要素で初期化
    ReDim X(10)
    ' 要素数を表示
    MsgBox UBound(X) - LBound(X) + 1
    ' 11要素だった配列を初期化せずに21要素に拡張
    ReDim Preserve X(20)
    ' 要素数を表示
    MsgBox UBound(X) - LBound(X) + 1
End Sub

'------------------------------------------<< End of Source >>--------------------------------------

配列の要素数は、宣言の段階で指定する方法の他、宣言時には不定のまま(カッコだけ)にし、ReDimステートメントで動的に配列要素数を割り当てる方法があります。また、このReDimステートメントに「Preserve」を付けると配列に格納した値を温存したまま配列の要素数を変更できます。但し、「Y」のような複数次元の場合、「Preserve」で配列の要素数が変更できるのは、最後の次元のみです。

「定数」利用する配列の生成


'***************************************************************************************************
'   配列処理サンプル                                                Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'03/06/29(1.00)新規作成
'16/11/19(1.10)*.xlsm化
'20/02/11(1.11)コード整理等
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ シート上のボタン起動処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST4
'* 機能  :配列処理のサンプル
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2003年06月29日
'* 作成者 :井上 治
'* 更新日 :2016年11月19日
'* 更新者 :井上 治
'* 機能説明:配列で定数(のようなもの)を作る例
'* 注意事項:
'***************************************************************************************************
Sub TEST4()
    '-----------------------------------------------------------------------------------------------
    Dim vntArray As Variant                                 ' 配列変数
    Dim lngIx As Long                                       ' 配列用INDEX
    ' 列番号からA1方式の列を判定するようなケース
    vntArray = Array(, "A", "B", "C", "D", "E", "F", "G")
    ' 配列要素を順にループ
    For lngIx = 1 To UBound(vntArray)
        MsgBox lngIx & "列目は" & vntArray(lngIx) & "列です。"
    Next lngIx
End Sub

'------------------------------------------<< End of Source >>--------------------------------------

配列で「定数」のようなものを作成したい場合は、Variant型で変数を宣言し、Array関数を使います。
実際は「定数」ではなく、配列変数ですが、再度別の値を割り当てない限りは「定数」と同じです。このサンプルでは、「0」番目の配列要素を無視するために、カッコ内の先頭にいきなりカンマを入れています。

私は昔は「COBOL言語」だったので「0」番目から始まる配列が苦手だったのでこのような記述になっていますが、現在ではそのようなことはありません。 要素開始を「1」にするのであれば「Option Base 1」をモジュールの先頭に書く方法もあるのですが、単に「慣れ」の問題とすれば良いものだと思います。 他の言語に乗り換える場合でも「0」始まりに慣れておいた方が良いでしょう。

2次元配列の操作


'***************************************************************************************************
'   配列処理サンプル                                                Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'03/06/29(1.00)新規作成
'16/11/19(1.10)*.xlsm化
'20/02/11(1.11)コード整理等
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ シート側からの起動処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST5
'* 機能  :2次元配列操作のサンプル(連番を2次元配置)
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2003年06月29日
'* 作成者 :井上 治
'* 更新日 :2016年11月19日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub TEST5()
    '-----------------------------------------------------------------------------------------------
    Dim tblVal(9, 9) As Long                        ' Long型の配列(10×10要素)
    Dim lngIx1 As Long                              ' テーブルINDEX
    Dim lngIx2 As Long                              ' テーブルINDEX
    Dim lngCnt As Long                              ' 件数カウンタ
    ' 1次元目(行)のループ
    For lngIx1 = 0 To 9
        ' 2次元目(列)のループ
        For lngIx2 = 0 To 9
            ' カウンタを加算
            lngCnt = lngCnt + 1
            ' 配列要素に値をセット
            tblVal(lngIx1, lngIx2) = lngCnt
        Next lngIx2
    Next lngIx1
     ' ある要素の値を表示
    MsgBox tblVal(4, 9)
End Sub

'------------------------------------------<< End of Source >>--------------------------------------

私の経験からも3次元以上の配列を扱うプログラムに出会うことはほとんどありませんでしたが、2次元配列については「日常茶飯事」のように利用しています。
このサンプルでは1次元目(通常は「行」方向と考えれば良いでしょう)のループの中で、 2次元目(通常は「列」方向と考えれば良いでしょう)のループがあるという構造になっています。
つまり、1次元目が1進むごとに2次元目の09が繰り返されます。

最後にメッセージボックスで表示するのは5×10番目の要素なので「50」と表示されます。

JAG配列


'***************************************************************************************************
'   配列処理サンプル                                                Module1(Module)
'
'   作成者:井上治  URL:https://www.ne.jp/asahi/excel/inoue/ [Excelでお仕事!]
'***************************************************************************************************
'変更日付 Rev  変更履歴内容------------------------------------------------------------------------>
'03/06/29(1.00)新規作成
'16/11/19(1.10)*.xlsm化
'20/02/11(1.11)コード整理等
'***************************************************************************************************
Option Explicit

'***************************************************************************************************
'   ■■■ シート側からの起動処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST6
'* 機能  :2次元配列操作のサンプル(連番を2次元配置=JAG配列)
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2003年06月29日
'* 作成者 :井上 治
'* 更新日 :2016年11月19日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub TEST6()
    '-----------------------------------------------------------------------------------------------
    Dim tblFld(9) As Long                           ' Long型の配列(10要素)
    Dim tblVal(9) As Variant                        ' Variant型の配列(10要素)
    Dim lngIx1 As Long                              ' テーブルINDEX
    Dim lngIx2 As Long                              ' テーブルINDEX
    Dim lngCnt As Long                              ' 件数カウンタ
    ' 1次元目(行)のループ
    For lngIx1 = 0 To 9
        ' 内側配列の初期化
        Erase tblFld
        ' 2次元目(行)のループ
        For lngIx2 = 0 To 9
            ' カウンタを加算
            lngCnt = lngCnt + 1
            ' 配列要素に値をセット
            tblFld(lngIx2) = lngCnt
        Next lngIx2
        ' JAG配列にセット
        tblVal(lngIx1) = tblFld
    Next lngIx1
     ' ある要素の値を表示
    MsgBox tblVal(4)(9)
End Sub

'------------------------------------------<< End of Source >>--------------------------------------

最初の方で説明した「但し、Yのような複数次元の場合、Preserveで配列の要素数が変更できるのは最後の次元のみです。」ということに疑問を持った方はすばらしい着眼点をお持ちの方だと思います。

一般的な「表」というイメージを配列のデータで扱い場合は、1次元目が「行」、2次元目が「列」ですから、 この規則だと「列」方向しか要素数の変更ができないことになります。 実際にはデータ件数で要素数を変更したいのは「行」なのであって、「列」はほとんどの場合項目が固定されています。

これを解決するのが「JAG配列」です。
サンプルコードを見て解るように2次元配列としての宣言はなく、縦横それぞれを1次元配列で宣言し「配列の中に配列を格納する」という動作をさせるものです。
最後の「ある要素の値を表示」の所の記述が前の「TEST5」と違っていることがこの方法の着目点です。
この方法であれば、1次元目の「tblVal」も実体は1次元配列なのでPreserveによる配列の要素数変更が可能です。

ReDimステートメント利用時のご注意
ReDimステートメント」利用時には注意事項があります。
ReDimステートメント」は先に宣言をしておいた配列変数に対して要素数の変更を行なう目的で利用するのが正しいのですが、 例えばこのページの先頭の「TEST3」プロシージャの場合だと、


'***************************************************************************************************
'   ■■■ シート上のボタン起動処理 ■■■
'***************************************************************************************************
'* 処理名 :TEST3
'* 機能  :配列処理のサンプル
'---------------------------------------------------------------------------------------------------
'* 返り値 :(なし)
'* 引数  :(なし)
'---------------------------------------------------------------------------------------------------
'* 作成日 :2003年06月29日
'* 作成者 :井上 治
'* 更新日 :2016年11月19日
'* 更新者 :井上 治
'* 機能説明:
'* 注意事項:
'***************************************************************************************************
Sub TEST3()
    '-----------------------------------------------------------------------------------------------
    Dim V(10) As Long                           ' Long型の配列(11要素)
    Dim W(1 To 10) As Long                      ' Long型の配列(10要素)
    'Dim X() As Long                             ' Long型の配列(要素数不定) ←※コメントにしてみた!
    Dim Y(1 To 10, 1 To 10) As Long             ' Long型の配列(10×10要素)
    ' 不定要素数だった変数を11要素で初期化
    ReDim X(10)
    ' 要素数を表示
    MsgBox UBound(X) - LBound(X) + 1
    ' 11要素だった配列を初期化せずに21要素に拡張
    ReDim Preserve X(20)
    ' 要素数を表示
    MsgBox UBound(X) - LBound(X) + 1
End Sub

このように「先に宣言しておいた配列変数」を忘れてしまっていてもコンパイルエラーにもならずそのまま動いてしまいます。
本来「ReDimステートメント」は、「動的配列変数に対する記憶域領域の再割り当て」と説明されており、初回の変数宣言にはならないはずなのに、 現実としてはこのように動作します。



先に変数宣言がある場合「ReDimステートメント」ではデータ型指定は行なわないので、このような「宣言忘れ」の場合はVariant型で扱われることになります。



調べてみると、Microsoftの資料には「変数宣言が存在しない場合は、宣言型ステートメントとして動作する」と説明されていますが、
間違っても「あえて先の変数宣言を省略」して用いるようなことは行なわないようにご注意下さい。

「配列」を制するはVBAを制す!?   初心者の方にいきなりこのような発言は申し訳ありませんが、VBAに限らずコンピュータ上の開発言語を扱う場合、「配列」を避けて通ることはまず不可能です。
Dictionaryオブジェクトがあるじゃないか」なんて考えている方もいるかも知れませんが、あれは入れた順に固定されるし、一次元しかありませんから応用が利かないでしょう。嫌いだからといって「配列」の代替策で使うのは誤りです。
この次の次のページでも「配列」の説明はしていますし、他にも何度も出てきますから「苦手」とせずにチャレンジして下さい。