VBAで配列を使う
仕事の効率化を行うために、Excelマクロを使って、情報(データ)を収集したりまとめたりすることが多いのですが、同様のデータを取り扱う場合、配列を使うことが多いです
■配列とは
配列とは下駄箱やロッカーのように同じ大きさの入れ物が沢山並んだ1つの固まりというイメージでしょうか?
基本的には、同じ大きさのデータを順番に格納して、順番に同じ処理していくために利用します
情報処理の世界では、決まった数のデータを処理するということもありますが、多くの場合、そのデータ数が決まっていないことがあり、そのデータを格納するための配列は動的にサイズを用意しなければなりません
空いている下駄箱がなければ、新しく作って全ての靴を下駄箱に収納するというようなことが、情報処理では行われています(例えが悪いw)
■配列を作成
VBAで配列を作るのは簡単です
Dimステートメントで宣言すればいいだけなのですが、宣言する際に配列数をしていするかしないかは自由だぁーーー!w
・ReDimステートメント
VBAで動的な配列と言うと以下のようなReDimステートメントのコードを思い浮かべる人が多いでしょう!
Sub Sample1() Dim a() As Integer ' 配列数を指定しない Dim b(3) As Integer ' 配列数を指定する Dim i As Integer ' 配列数を変更する(既存データを初期化) ReDim a(3) a(0) = 0 a(1) = 1 a(2) = 2 a(3) = 3 Debug.Print UBound(a) ' 「3」を返す For i = 0 To UBound(a) Debug.Print a(i) Next i ' 配列数を変更する(既存データを残す) ReDim Preserve a(5) a(4) = 4 a(5) = 5 Debug.Print UBound(a) ' 「5」を返す For i = 0 To UBound(a) Debug.Print a(i) Next i ' 配列数を変更する(既存データを初期化) ReDim a(8) Debug.Print UBound(a) ' 「8」を返す For i = 0 To UBound(a) Debug.Print a(i) Next i End Sub
※Preserveオプションは、配列のサイズを変更する際に、既存の配列のデータを保持してくれます
※しれっと使っているUBound関数は配列の添え字の最大値を返してくれます
ReDimステートメントは、多次元の配列のサイズ変更も非常に簡単なので、重宝されています
・Array関数
初期値を設定した配列を作成する場合には、以下のようにして、Array関数を使用することが多いです
参考サイト:「Array 関数 (Visual Basic for Applications) | Microsoft Docs」
Sub Sample2() Dim week As Variant Dim day As Variant week = Array("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun") Debug.Print UBound(week) ' 「6」を返す ' Variant型の配列はFor~Eachステートメントで取り出せる For Each day In week Debug.Print day Next End Sub
Array関数で作成した配列には以下の特徴があります
- Variant型の配列を作成して返す
- ReDimステートメントで配列数が変更できる
Array関数の実行結果をブレークポイントを設定して、変数内を確認してみましょー
Variant型なので、サンプルのようにFor~Eachステートメントを使用して取り出すことが可能です
ただし、Variant型というのはどんな型でも代入できるので、その変数が何を意味するデータなのかが判り辛いので、個人的にはあまり好きではありませんw
■複雑な操作
配列を動的に作成する方法としては、上記に記載した方法で問題はないのですが、以下のように配列に対して複雑な操作をする場合には適していません
- データを途中に追加する
- 途中のデータを削除する
- データを検索する
配列に対して複雑な操作が可能なオブジェクトには、以下のようなものがあります!
- Collectionオブジェクト
- Dictionaryオブジェクト
このオブジェクトについて、簡単に説明していきます
・Collectionオブジェクト
このオブジェクトは配列に格納するデータを1つの関連性のあるグループとして扱うことで簡単に操作することができます
実際には、必ずしも同じ型のデータである必要はないのですが、使い方としては、同じ型のデータを格納して、For~Eachステートメントで操作するのが一般的です
あまり知られていない(?)かも知れませんが、CollectionオブジェクトでもItemとKeyを関連付けて格納することができます
ただし、For~Eachステートメントで取り出す場合は、Keyは関係ないので、個人的にはあまりKeyを指定することはありません
Collectionオブジェクトについては以下の記事も参考にしてください
・Dictionaryオブジェクト
このオブジェクトは配列に格納するデータをKeyとItemのペアにして扱っていますので、一意のKeyでItemを取り出すことが可能です
有り難い機能としては、Dictionaryオブジェクト内に格納したKeyが既に存在するかどうかを判定するExistsメソッドがあります!
そのため、「存在しない場合にのみ追加する」というデータの管理方法が容易に実現することができます
そして、For~Eachステートメントで取り出す場合は、ItemではなくKeyが取得されるので注意が必要です
・サンプル
それぞれのオブジェクトの生成方法について、簡単なサンプルを紹介します
Sub Sample3() Dim a As New Collection ' Collectionオブジェクト Dim b As Object ' Dictionaryオブジェクト ' Dictionaryオブジェクトの生成 Set b = CreateObject("Scripting.Dictionary") ... End Sub
生成方法は以下のように違います
- Collectionオブジェクトはそのまま宣言で記載できます
- DictionaryオブジェクトはCreateObjectで生成します
特に、Collectionオブジェクトの場合は、宣言時にNewを指定した使い方を多く見かけます
個人的には以下のSample4のように、宣言ではNewを使用せずに、Set時に使用して生成するようにしています
Sub Sample4() Dim a As Collection ' Collectionオブジェクト Dim b As Object ' Dictionaryオブジェクト ' Collectionオブジェクトの生成 Set a = New Collection ' Dictionaryオブジェクトの生成 Set b = CreateObject("Scripting.Dictionary") ... End Sub
※ま、お好みの方法で良いと思いますけどねw
■小ネタ
ReDimステートメントを使用して、ちょっとしたサンプルを記載します
Sub Sample5() Dim week() As String Dim day As Variant ReDim week(6) week(0) = "Mon" week(1) = "Tue" week(2) = "Wed" week(3) = "Thu" week(4) = "Fri" week(5) = "Sat" week(6) = "Sun" Debug.Print UBound(week) ' 「6」を返す ' Variant型の配列はFor~Eachステートメントで取り出せる For Each day In week Debug.Print day Next End Sub
最初のサンプル(Sample1)では、配列を取り出す際に、For~Nextステートメントで記載しましたが、配列であれば、For~Eachステートメントで取り出すこともできます
当然、Array関数のように、Variant型ではなく、String型の配列でも同じです
Sub Sample6() Dim week() As Variant Dim day As Variant ReDim week(6) week(0) = "Mon" week(1) = "Tue" week(2) = "Wed" week(3) = "Thu" week(4) = "Fri" week(5) = "Sat" week(6) = "Sun" Debug.Print UBound(week) ' 「6」を返す ' Variant型の配列はFor~Eachステートメントで取り出せる For Each day In week Debug.Print day Next End Sub
ちなみに、Variant型の配列宣言は、括弧を付けても(Sample6)付けなくても(Sample2)宣言が可能ですが、String型等(Sample5)は括弧を付けないと、ReDimステートメントでエラーが発生します
■最後に
かなりザックリとVBAでの配列の使い方を紹介しましたw
次回は、CollectionオブジェクトやDictionaryオブジェクトの詳細な使い方について、勉強していこうと思います!
ではでは