【Excel VBA入門】配列とは?二次元配列を使った高速化手法も解説
こんにちは、自動化ツール開発担当の吉池(@valmore_myoshi)です。
今回は配列を取り上げて解説します。同じ種類のデータをまとめて扱うときに便利な配列ですが、Excel操作の高速化にも役立ちます。
本記事では、配列の基本的な使い方はもちろん、二次元配列を利用したExcel操作の高速化手法についても解説します。VBAでExcel操作を自動化するときにお役立てください。
目次
配列とは
配列を使うと同じ種類のデータをまとめて扱えます。まとめて扱うデータ数(=要素数)を最初に決めて使います。
Dim 配列名(サイズ) as データ型
例えば、いくつか文字列をまとめて配列にしてみましょう。今回は4つの文字列を格納する配列を宣言し、一つずつ代入しています。
Dim foodList(3) As String foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン"
配列を宣言したら、インデックス番号を指定して初期値を入れていきます。インデックス番号とは、配列内の個々の要素につけられた番号で、要素を識別する役目があります。
インデックス番号は0から始まることに要注意。サイズを3と指定した場合は0, 1, 2, 3の計4つの要素をもつ配列が作られます。
配列のインデックス番号は指定できる
配列のインデックス番号は0から始まる連番以外の値に変更することも可能です。例えば1から始めたければ下記のように書きます。
Dim foodList(1 To 4) As String foodList(1) = "りんご" foodList(2) = "みかん" foodList(3) = "桃" foodList(4) = "メロン"
要素数を取得するにはLBoundとUBoundを使う
配列の要素数は最初に宣言しますが、Forループなどで要素数を再度取得したいと思うことはよくあります。そんなときはLBound関数とUBound関数を使いましょう。
- LBound関数:配列の先頭のインデックス番号を取得
- UBound関数:配列の末尾のインデックス番号を取得
書き方は下記の通り。2つ目の引数である次元を省略すると1次元目のインデックスを取得します。
‘ LBound関数の書き方 LBound(配列名, インデックス番号を取得したい次元) ‘ UBound関数の書き方 UBound(配列名, インデックス番号を取得したい次元)
つまり、「UBound関数で取得したインデックス番号」から「LBound関数で取得したインデックス番号」を減算して1を加算すれば配列の要素数を求められます。とても簡単ですね。
Dim foodList(0 To 3) As String foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン" Debug.Print "先頭のインデックス番号:", LBound(foodList) Debug.Print "末尾のインデックス番号:", UBound(foodList) Debug.Print "配列の要素数:", UBound(foodList) - LBound(foodList) + 1
他にもForループを使うときにもLBound関数とUBound関数は役立ちます。Forループの初期値と最大値の指定方法を見てみましょう。
Dim foodList(0 To 3) As String Dim i As Integer foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン" For i = LBound(foodList) To UBound(foodList) Debug.Print i, foodList(i) Next
可変長配列の作り方
これまでは固定長配列の作り方を解説しましたが、動的に要素数を変える可変長配列も作れます。可変長配列はサイズを指定しないで配列を宣言します。
Dim 配列名() as データ型
要素数を変えたいときはReDim
サイズ指定をしないで宣言することで可変長配列を作れますが、実際に配列を使うときにはReDimステートメントでサイズを指定しなければなりません。
Dim foodList() As String ‘ 配列のサイズを3に指定 ReDim foodList(3) foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン"
ReDimステートメントで要素数を変えると配列の値が消えることに注意が必要です。下記の例では、ReDimでサイズ指定したあとに文字列を代入し、再度ReDimで要素数を変更しています。その結果、配列に代入した値が消えてしまいました。
Dim foodList() As String Dim i As Integer ' サイズを3に指定 ReDim foodList(3) foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン" ' サイズを4に再度指定 ReDim foodList(4) foodList(4) = "バナナ" ' 配列要素の値を出力 For i = LBound(foodList) To UBound(foodList) Debug.Print i, foodList(i) Next
値を保持したいならReDim Preserve
配列要素の値を保持したまま要素数を変更したい場合はReDim Preserveステートメントを使います。
Dim foodList() As String Dim i As Integer ' サイズを3に指定 ReDim foodList(3) foodList(0) = "りんご" foodList(1) = "みかん" foodList(2) = "桃" foodList(3) = "メロン" ' 値を保持したままサイズを4に再度指定 ReDim Preserve foodList(4) foodList(4) = "バナナ" ' 配列要素の値を出力 For i = LBound(foodList) To UBound(foodList) Debug.Print i, foodList(i) Next
動的にサイズが変わる配列を作るならCollectionオブジェクトも要チェック!配列のようにサイズを宣言する必要はありません。
【Excel VBA入門】Collectionとは?サイズ変更に強いリストの作り方
手軽に配列を作る方法
配列を宣言し、地道にインデックス番号を使って初期値を代入するのは手間がかかります。もっと簡単に配列を作る方法としてArray関数とSplit関数を覚えておきましょう。
宣言がいらないArray関数を使う
Array関数は配列に入れたい値を引数に入れるだけで配列を返してくれる優れもの。ただし、Array関数はVariant型の配列を返すのでデータ型を指定できない欠点があります。
Dim foodList() As Variant Dim i As Integer foodList = Array("りんご", "みかん", "桃", "メロン") ' 配列要素の値を出力 For i = LBound(foodList) To UBound(foodList) Debug.Print i, foodList(i) Next
区切られた文字列を配列に変換するにはSplit関数
CSVのようにカンマで区切られた文字列であれば、Split関数で配列に変換できます。
Split(文字列, 区切り文字)
例えば、カンマで区切られた文字列を配列に変換するには下記のように書きます。
Dim foodList() As String Dim i As Integer foodList = Split("りんご,みかん,桃,メロン", ",") ' 配列要素の値を出力 For i = LBound(foodList) To UBound(foodList) Debug.Print i, foodList(i) Next
二次元配列を使った高速化
二次元配列は縦と横の2つの次元をもった配列です。下記イメージからもわかる通り、Excelのセルと同じように値を管理できるのがポイント。「まとまった値」を「高速」にセルへ書き込めます。
二次元配列の宣言は下記のように書きます。
Dim 配列名(1次元目のサイズ, 2次元目のサイズ) as データ型
書き込み回数を減らすのが高速化のカギ
VBAプログラムを高速化するカギはできる限り書き込み回数を減らすことです。なぜなら書き込みは負荷が高い処理だから。セルに一つずつ値を書き込むのではなく、二次元配列を使って一度に処理することで書き込み回数を減らしましょう。
例えば、下記のように書き込む例を取り上げてみます。
まずはダメな例から。Forループを重ねて二次元配列から値を一つずつ取り出し、セルに書き込んでいます。書き込み回数は1次元目の要素数と二次元目の要素数合わせて8回です。
Dim foodList(1, 3) As String Dim i As Integer, j As Integer foodList(0, 0) = "りんご" foodList(0, 1) = "みかん" foodList(0, 2) = "桃" foodList(0, 3) = "メロン" foodList(1, 0) = "バナナ" foodList(1, 1) = "すいか" foodList(1, 2) = "キウイ" foodList(1, 3) = "ぶどう" ' 二次元配列から一つずつ値をセルに入力 For i = LBound(foodList, 1) To UBound(foodList, 1) For j = LBound(foodList, 2) To UBound(foodList, 2) Cells(i + 1, j + 1).Value = foodList(i, j) Next Next
書き込み回数を1回に抑え、処理を高速化するには下記のように書きます。二次元配列のサイズと同じセル範囲を用意し、代入するだけで書き込み回数を1回にしています。
Dim foodList(1, 3) As String Dim i As Integer, j As Integer foodList(0, 0) = "りんご" foodList(0, 1) = "みかん" foodList(0, 2) = "桃" foodList(0, 3) = "メロン" foodList(1, 0) = "バナナ" foodList(1, 1) = "すいか" foodList(1, 2) = "キウイ" foodList(1, 3) = "ぶどう" ' 二次元配列と同じセル範囲を用意し、一度に書き込み Range("A1:D2").Value = foodList
セル範囲を計算するのが面倒であれば、基準となるセルさえ決めてしまえば範囲を計算できます。例えば、セルB2を基準にすると、Resizeプロパティを使って配列の要素数からセル範囲を求められます。
Range("B2").Resize(UBound(foodList, 1) + 1, UBound(foodList, 2) + 1).Value = foodList
まとめ
配列は同じ種類のデータをまとめて扱える便利な入れ物です。VBAでは繰り返し同じデータを処理することが大半なので、配列の使い方を覚えておいて損はありません。
配列の扱いに慣れた後は、二次元配列を使った高速化にもチャレンジしてみてください。データ量が大きければ大きいほど高速化の効果が生きてきます。