はじめに
Excelでマクロを書いていると、稀に配列の要素数(長さ)を求めたい時があります。
Javaであれば {配列}.length
で簡単に求められますが、VBAの場合はそこまで簡単ではありません。
UboundとLbound
VBAにはUboundとLboundという関数がありますが、これらの関数は以下のように「添え字の最大値/最小値を求める」という処理になっています。
配列の要素数に近いものではありますが、残念ながら配列の要素数そのものではありません...
関数名 | 処理内容 |
---|---|
Ubound(配列) | 配列の添え字の最大値を返す。 |
Lbound(配列) | 配列の添え字の最小値を返す。 |
自作関数で配列の要素数を求める
以下のコードの「CalcArrayLength」という自作の関数が、配列の要素数を求める処理になります。
工夫した点は「引数をVariant型にすることで、配列がInteger型でもString型でも関係なく処理できる」という部分です。
引数をVariant型にすると、配列以外が引数となる恐れがあるため、IsArray関数で配列であるかどうかをチェックしています。
また、引数が「Redimで初期化されていない動的配列」の時は、On Error GoToを活用してエラーを吸収するようにしました。
「動的配列が初期化済みかどうかはSgn関数を使って調べる」と書かれているサイトも多いですが、@satoko138 さんのこちらの記事によると「Sgn関数を使って判定するのは不適切」ということなので、それを踏まえた実装にしてみました。
Option Explicit
Sub Test()
' 要素数が11の配列(※添え字が0~10)
Dim ary1(10) As Integer
MsgBox "ary1の長さ:" & CalcArrayLength(ary1)
' 要素数が10の配列(※添え字が1~10)
Dim ary2(1 To 10) As String
MsgBox "ary2の長さ:" & CalcArrayLength(ary2)
' 初期化済みの動的配列(※添え字が0~5)
Dim ary3() As String
ReDim ary3(5)
MsgBox "ary3の長さ:" & CalcArrayLength(ary3)
' 引数が初期化されていない動的配列→-1が返される
Dim ary4() As String
MsgBox "ary4の長さ:" & CalcArrayLength(ary4)
' 引数が配列以外→-100が返される。
MsgBox "配列以外:" & CalcArrayLength("abc")
End Sub
' 配列の要素数を求める。
'
' ary:対象となる配列。
' return:配列の要素数。引数として初期化されていない配列を指定した時は-1、配列以外を指定した時は-100を返す。
Function CalcArrayLength(ary As Variant) As Integer
If (IsArray(ary)) Then
If (IsInitialized(ary)) Then
CalcArrayLength = UBound(ary) - LBound(ary) + 1
Else
CalcArrayLength = -1
End If
Else
CalcArrayLength = -100
End If
End Function
' 配列が初期化されているかをチェックする。
'
' ary:対象となる配列。
' return:配列が初期化済みならTrue、そうでなければFalseを返す。
Function IsInitialized(ary As Variant) As Boolean
On Error GoTo NOT_INITIALIZED_ERROR
Dim length As Long: length = UBound(ary) ' 動的配列が初期化されていなければ、ここでエラーが発生する。
IsInitialized = True
Exit Function
' 配列が初期化されていない場合はここに飛ばされる。
NOT_INITIALIZED_ERROR:
IsInitialized = False
End Function