VBA配列の使い方|初期化・2次元配列・ループからセル範囲の高速処理まで解説

  • 2026.10.08
       
【Excel VBA】配列の宣言・初期化・使用方法を解説

VBAの配列(Array)は、複数の値を1つの変数名でまとめて扱う仕組みです。Excel VBAでは、セル範囲の値を配列にまとめて読み込み、配列の中で計算してから一括で書き戻すことで、シートへの読み書き回数を減らし、処理を速くできます。

この記事では、「配列とは何か」という基本から、宣言・初期化、セル範囲との一括処理、配列を使った高速化、コピー・並べ替え・検索などの操作、よくあるエラーの対処法までを、基礎 → 実務 → 応用 → トラブル解決の順に解説します。

目次

VBAの配列とは

複数の値を1つの名前でまとめて扱う仕組み

通常の変数には、値を1つしか入れられません。たとえば果物の名前を3つ扱いたい場合、変数を3つ用意する必要があります。

【通常の変数で3つの値を扱う場合】

Dim fruit1 As String, fruit2 As String, fruit3 As String

fruit1 = "りんご"
fruit2 = "みかん"
fruit3 = "バナナ"

配列を使うと、同じ3つの値を1つの名前(fruits)と番号で管理できます。

【配列で3つの値を扱う場合】

Dim fruits(2) As String

fruits(0) = "りんご"
fruits(1) = "みかん"
fruits(2) = "バナナ"

配列に入っている1つ1つの値を要素、要素を指定するための番号を添字(インデックス)と呼びます。fruits(1)と書けば「fruitsの1番の要素(みかん)」を取り出せます。

番号で要素を指定できるため、ループ処理と組み合わせると、値が100個や1万個に増えても同じ短いコードで処理できます。

Excel VBAで配列が役立つ場面

Excel VBAで配列が特に役立つのは、大量のセルを読み書きする処理です。

VBAからセルの値を1つずつ読み書きすると、そのたびにExcelのシートにアクセスするため、データが多いほど時間がかかります。そこで、対象のセル範囲をいったん配列にまとめて読み込み、配列の中で計算や加工をしてから、最後にまとめてシートへ書き戻します。こうするとシートへのアクセスは「読み込み1回・書き込み1回」で済みます。

ほかにも、次のような場面で配列がよく使われます。

  • 表データ(行×列)をまとめて集計・加工したい
  • 条件に合う行だけを抜き出して別の場所に出力したい
  • 決まった選択肢(曜日・部署名など)をまとめて管理したい

具体的なコードは「配列を使ってExcelの処理を速くする」で紹介します。

配列を使ってExcelの処理を速くする

静的配列と動的配列の違い


静的配列は要素数が最初から決まっていて、動的配列はあとから要素数を変更できることを示す図

VBAの配列には大きく分けて2つの種類があります。宣言したときに要素数が決まり、あとから変更できないものを静的(固定長)配列、宣言したあとで要素数を増減できるものを動的(可変長)配列といいます。

  • 静的配列:曜日(7つ)のように、要素数が最初から決まっているときに使う
  • 動的配列:シートの行数のように、実行するまで要素数が分からないときに使う

それぞれの宣言方法は「配列を宣言して値を入れる」で解説します。

まずは配列を動かしてみよう

サンプルコードを実行する手順

Excelで[Alt]+[F11]キーを押してVBE(Visual Basic Editor)を開き、メニューの[挿入]→[標準モジュール]を選んで、次のコードを貼り付けます。コードの中にカーソルを置いて[F5]キーを押すと実行できます。

【サンプルコード】

Sub FirstArray()
    Dim fruits(2) As String

    fruits(0) = "りんご"
    fruits(1) = "みかん"
    fruits(2) = "バナナ"

    MsgBox fruits(0) & "、" & fruits(1) & "、" & fruits(2)
End Sub

【実行結果(メッセージボックスの表示)】

りんご、みかん、バナナ

この記事のサンプルコードは、モジュールの先頭にOption Explicit(変数の宣言を強制する設定)を書いた状態でも動くように、使う変数をすべてDimで宣言しています。VBEの基本操作や変数の宣言については、以下の記事も参考にしてください。

▼こちらもチェック▼

配列の番号(添字)は0から始まる

Dim fruits(2) As Stringの「2」は要素の数ではなく、最後の番号です。番号は0から数えるため、fruits(0)・fruits(1)・fruits(2)の3つの要素が用意されます。

「2と書いたのに要素は3つ」という点は、初心者が最もつまずきやすいポイントです。存在しないfruits(3)を指定すると「インデックスが有効範囲にありません」というエラーになります(詳しくは「配列でよくあるエラーと対処法」で解説します)。

配列でよくあるエラーと対処法

番号を1から始めたい場合は、次の章で紹介する1 To 3のように、最初の番号も指定して宣言します。

配列を宣言して値を入れる

要素数が決まっている配列を宣言する

静的配列は、括弧の中に最後の番号を書いて宣言します。

【静的配列の基本構文】

Dim 配列名(最後の番号) As データ型
Dim 配列名(最初の番号 To 最後の番号) As データ型

最初の番号を省略すると、番号は0から始まります(モジュールの先頭にOption Base 1を書いた場合は1から始まります)。1 To 3のように最初の番号も書いておくと、Option Baseの設定に左右されず、「1番〜3番の3つ」と直感的に読めます。

【サンプルコード】

Sub DeclareWithLowerBound()
    Dim scores(1 To 3) As Long

    scores(1) = 80
    scores(2) = 65
    scores(3) = 92

    MsgBox "最初の番号:" & LBound(scores) & vbCrLf & "最後の番号:" & UBound(scores)
End Sub

【実行結果(メッセージボックスの表示)】

最初の番号:1
最後の番号:3

LBoundは配列の最初の番号、UBoundは最後の番号を返す関数です。使い方は「配列の要素数を取得する」で詳しく解説します。

配列の要素数を取得する

要素数をあとで決める配列を宣言する

動的配列は、括弧の中に何も書かずに宣言します。

【動的配列の基本構文】

Dim 配列名() As データ型

この時点では要素が1つもないため、値を入れる前にReDimステートメントで要素数を決める必要があります。ReDimの使い方は「動的配列で要素数を変更する」で解説します。

動的配列で要素数を変更する

複数の値をまとめて代入する

要素に1つずつ値を代入するほかに、Array関数を使うと複数の値を一度に設定できます。

【サンプルコード】

Dim arr2() As Variant
arr2 = Array(1, 2, 3)

Array関数が返すのはVariant型の値です。そのため、代入先はDim arr2() As Variant(Variant型の動的配列)かDim arr2 As Variant(Variant型の変数)にします。Dim arr2() As Stringのような型付きの配列に代入すると実行時エラー「型が一致しません。」、Dim arr2(2) As Variantのような静的配列に代入するとコンパイルエラー「配列には割り当てられません。」になります。

Array関数で作った配列の番号は、通常0から始まります(Option Base 1を書いた場合は1から始まります)。

配列を初期化する

配列に代入した値をリセット(初期化)したい場合は、Eraseステートメントを使います。

【基本構文】

Erase 配列名

Eraseの動きは、静的配列と動的配列で異なります。

  • 静的配列:要素数はそのままで、すべての要素がデータ型ごとの初期値(数値は0、文字列は長さ0の文字列「””」、オブジェクトはNothing、VariantはEmpty)に戻る
  • 動的配列:要素そのものがなくなり、使っていたメモリが解放される。再び使うにはReDimで要素数を決め直す

なお、Variant型の配列にArray()(引数なし)を代入すると、要素数0の配列になります。このときUBoundは「-1」を返し、Eraseした動的配列のようにエラーにはならない点が異なります。

【サンプルコード】

Dim sample() As Variant
sample = Array()

一次元配列と二次元配列

一次元配列は「1列のリスト」

ここまで紹介したfruits(2)のように、番号を1つだけ使う配列を一次元配列といいます。値が1列に並んだリストのイメージで、「りんご・みかん・バナナ」のような名前の一覧や、点数の一覧などを扱うのに向いています。

二次元配列は「行と列の表」

番号を2つ使う配列を二次元配列といいます。Excelの表のように「行」と「列」で値を管理でき、配列名(行の番号, 列の番号)の形で要素を指定します。

【基本構文】

Dim 配列名(1次元目の最後の番号, 2次元目の最後の番号) As データ型
Dim 配列名(1 To 行数, 1 To 列数) As データ型

ほかのプログラミング言語では、二次元配列を「配列の中に配列を入れたもの(array[i][j])」として扱うことがあります。一方、VBAの二次元配列はarray(i, j)のように1つの括弧に番号を2つ書く表(行列)の形で、どの行も同じ列数を持ちます。

実際に二次元配列を扱ってみましょう。以下のコードは、3行×3列の配列に「A1」〜「C3」の文字列を入れて、イミディエイトウィンドウ(VBEで[Ctrl]+[G]キーを押すと表示される画面)に出力します。

【サンプルコード】

Sub Sample2DArray()
    Dim arr(1 To 3, 1 To 3) As Variant
    Dim i As Long, j As Long

    ' 二次元配列に自動的に値を代入する
    For i = LBound(arr, 1) To UBound(arr, 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            ' Chr(64 + j) を使って、列番号 1=> "A", 2=> "B", 3=> "C" に変換
            arr(i, j) = Chr(64 + j) & i
        Next j
    Next i

    ' 代入された各要素をイミディエイトウィンドウに出力
    For i = LBound(arr, 1) To UBound(arr, 1)
        For j = LBound(arr, 2) To UBound(arr, 2)
            Debug.Print "arr(" & i & ", " & j & ") = " & arr(i, j)
        Next j
    Next i
End Sub

【実行結果(イミディエイトウィンドウの表示)】

arr(1, 1) = A1
arr(1, 2) = B1
arr(1, 3) = C1
arr(2, 1) = A2
arr(2, 2) = B2
arr(2, 3) = C2
arr(3, 1) = A3
arr(3, 2) = B3
arr(3, 3) = C3

LBound(arr, 1)・UBound(arr, 1)のように2つ目の引数に次元の番号を指定すると、その次元の最初と最後の番号を取得できます。1が行、2が列に対応します。

セルの位置も「行」と「列」の2つの番号で表せるため、二次元配列はセル範囲と相性がよく、実務ではセル範囲をまとめて読み書きするときによく使われます(「セル範囲と配列をまとめてやり取りする」で解説します)。セルを行番号・列番号で指定するCellsプロパティについては、こちらの記事もご覧ください。

▼こちらもチェック▼
セル範囲と配列をまとめてやり取りする

配列の要素数を取得する

VBAには、配列の要素数を直接返す関数がありません。代わりに、最初の番号を返すLBound関数と、最後の番号を返すUBound関数を組み合わせて計算します。


インデックス0〜3の配列で、LBoundが最初の番号0、UBoundが最後の番号3を指すことを示す図

・LBound = 配列の「最初の番号」
・UBound = 配列の「最後の番号」

【基本構文】

要素数 = UBound(配列名) - LBound(配列名) + 1

' 二次元配列の場合は、2つ目の引数で次元を指定する
行数 = UBound(配列名, 1) - LBound(配列名, 1) + 1
列数 = UBound(配列名, 2) - LBound(配列名, 2) + 1

番号が0から始まる配列なら「UBound+1」でも要素数になりますが、1から始まる配列では1つ多くなってしまいます。LBoundを引く形で書いておけば、番号がいくつから始まっていても正しい要素数を取得できます。

【サンプルコード】

Sub GetArrayCount()
    Dim arr As Variant
    Dim lowerIndex As Long
    Dim upperIndex As Long
    Dim count As Long

    ' Array関数で4つの値を設定(番号は0〜3)
    arr = Array("Apple", "Banana", "Cherry", "Date")

    ' 最初の番号と最後の番号を取得
    lowerIndex = LBound(arr)
    upperIndex = UBound(arr)

    ' 要素数を計算(UBound - LBound + 1)
    count = upperIndex - lowerIndex + 1

    MsgBox "配列の要素数は " & count & " です。"
End Sub

【実行結果(メッセージボックスの表示)】

配列の要素数は 4 です。

ループで配列を処理する

配列の要素を順番に処理するときは、ループ(繰り返し処理)と組み合わせます。代表的な書き方は「For…Next」と「For Each…Next」の2つです。

番号を使って順番に処理する

For…Nextステートメントでは、ループ用の変数を番号として使い、要素に順番にアクセスします。

【基本構文】

For 変数名 = LBound(配列名) To UBound(配列名)
    ' 配列名(変数名) で順に要素にアクセス
Next 変数名

【サンプルコード】

Sub ArrayLoop()
    Dim scores As Variant
    Dim i As Long
    Dim total As Long

    scores = Array(80, 65, 92)

    For i = LBound(scores) To UBound(scores)
        total = total + scores(i)
    Next i

    MsgBox "合計:" & total
End Sub

【実行結果(メッセージボックスの表示)】

合計:237

このコードでは、ループの範囲をFor i = 0 To 2のような固定の数値ではなく、LBound(scores) To UBound(scores)と書いています。こう書くと、次のようなメリットがあります。

  • 配列のサイズが変わっても書き直さなくてよい:Arrayの値を4つ・5つに増やしても、ループの範囲が自動で合う
  • 開始番号を決め打ちしなくてよい:0始まりの配列でも、セル範囲から作った1始まりの配列でも同じ書き方で回せる
  • 範囲外エラーを防ぎやすい:存在しない番号にアクセスする「インデックスが有効範囲にありません」エラーが起きにくい

For…Nextの基本的な使い方は、以下の記事で詳しく解説しています。

▼こちらもチェック▼

すべての要素を順番に取り出す

For Each…Nextステートメントを使うと、番号を使わずに配列のすべての要素を先頭から順番に取り出せます。

【基本構文】

For Each 変数名 In 配列名
    ' 変数に要素の値が入るので、変数を使って処理
Next 変数名

【サンプルコード】

Sub ArrayForEach()
    Dim scores As Variant
    Dim score As Variant
    Dim total As Long

    scores = Array(80, 65, 92, 74, 88)

    For Each score In scores
        total = total + score
    Next score

    MsgBox "合計:" & total
End Sub

【実行結果(メッセージボックスの表示)】

合計:399

配列をFor Eachで回すときは、要素を受け取る変数(ここではscore)をVariant型で宣言します。

For Eachは番号を書かなくてよいぶん手軽ですが、変数scoreに入るのは要素のコピーです。scoreに別の値を代入しても配列の中身は変わらないため、要素を書き換えたいときはFor…Nextを使いましょう。また、セル範囲から読み込んだ二次元配列をFor Eachで回すと、1列目を上から下へ、次に2列目を上から下へという順にすべての要素が取り出されます(行ごとの順ではありません)。行と列を意識して処理したい場合は、For…Nextを二重にして書きましょう。

セル範囲と配列をまとめてやり取りする

ここからは、Excel VBAの実務で特によく使う「セル範囲と配列の受け渡し」を解説します。

セル範囲の値を配列に読み込む

Variant型の変数にRange("A1:B3").Valueのように複数セルの値を代入すると、セル範囲と同じ形の二次元配列になります。セルを1つずつ読む必要はありません。

セルA1〜B3に「A1」「B1」…のように、セル番地と同じ文字列が入力されているシートで、次のコードを実行してみましょう。

【サンプルコード】

Sub sampleArray1()
    Dim arr As Variant
    Dim msg As String

    arr = Range("A1:B3").Value

    msg = arr(1, 1) & "," & arr(1, 2) & vbCrLf _
        & arr(2, 1) & "," & arr(2, 2) & vbCrLf _
        & arr(3, 1) & "," & arr(3, 2) & vbCrLf

    MsgBox msg
End Sub

【実行結果】

セル範囲から作った配列は、1つ目の番号が行、2つ目の番号が列に対応します。また、Option Baseの設定に関係なく、番号は0ではなく1から始まります。セルと配列の対応は次のとおりです。

セル配列での書き方意味
A1arr(1, 1)範囲の1行目・1列目
B1arr(1, 2)範囲の1行目・2列目
A2arr(2, 1)範囲の2行目・1列目
B3arr(3, 2)範囲の3行目・2列目

注意したいのは、番号がシートの行番号・列番号ではなく「読み込んだ範囲の中での位置」だという点です。たとえばRange("C5:D7").Valueを読み込んだ場合、セルC5の値はarr(1, 1)に入ります。

実務では、データの行数が毎回変わることがほとんどです。その場合は、最終行を取得してから範囲を指定します。

【サンプルコード】

Dim data As Variant
Dim lastRow As Long

' A列の最終行を取得して、A1からB列の最終行までを読み込む
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
data = Range("A1:B" & lastRow).Value

' 行数と列数は UBound で確認できる
MsgBox "行数:" & UBound(data, 1) & " 列数:" & UBound(data, 2)

最終行の取得方法については、以下の記事で詳しく解説しています。

▼こちらもチェック▼

配列の値をセル範囲に書き込む

反対に、二次元配列をセル範囲のValueに代入すると、配列の値をまとめてシートに書き込めます。

【サンプルコード】

Sub sampleArray2()
    Dim arr(1 To 3, 1 To 2) As Variant
    Dim r As Long, c As Long

    For r = 1 To 3          ' 行
        For c = 1 To 2      ' 列
            arr(r, c) = r + 10 * (c - 1)
        Next c
    Next r

    Range("A1:B3").Value = arr
End Sub

【実行結果】

書き込むときは、セル範囲の大きさを配列の行数・列数に合わせるのがポイントです。範囲が配列より大きいと余ったセルに「#N/A」が入り、範囲が小さいと入りきらない値は書き込まれません。

配列の大きさに合わせて範囲を自動で決めたい場合は、Resizeプロパティを使うと便利です。

' セルA1を起点に、配列と同じ行数・列数の範囲へ書き込む
Range("A1").Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr

なお、書き込みに使う配列は、番号が0から始まる配列(例:Dim arr(2, 1))でも問題ありません。配列の先頭の要素が範囲の左上のセルに入ります。ただし、その配列に再びセル範囲の値を読み込むと、番号は1から始まる形に変わります。

1列だけ読み込んでも二次元配列になる

初心者がよく間違えるのが、Range("A1:A3")のように1列だけを読み込んだ場合です。見た目はリストでも、配列は「3行×1列」の二次元配列になるため、data(1)ではなくdata(1, 1)のように列の番号も書く必要があります。

【サンプルコード】

Sub ReadOneColumn()
    Dim data As Variant
    Dim i As Long
    Dim msg As String

    data = Range("A1:A3").Value   ' 3行×1列の二次元配列になる

    For i = LBound(data, 1) To UBound(data, 1)
        msg = msg & data(i, 1) & vbCrLf   ' data(i) ではなく data(i, 1)
    Next i

    MsgBox msg
End Sub

同じように、Range("A1:C1")のように1行だけを読み込んだ場合も「1行×3列」の二次元配列になり、data(1, 1)・data(1, 2)・data(1, 3)でアクセスします。

一方、Range("A1").Valueのようにセル1つだけを読み込んだ場合は、配列ではなく単なる値になります。範囲の大きさが実行時に変わるコードでは注意しましょう(「配列でよくあるエラーと対処法」で解説します)。

配列でよくあるエラーと対処法

セルの値を取得・設定するValueプロパティについては、以下の記事も参考にしてください。

▼こちらもチェック▼

配列を使ってExcelの処理を速くする

ここでは、A列に入った1万件の数値を2倍にしてB列に書き込む処理を例に、「セルを1つずつ処理する方法」と「配列を使う方法」を比べてみます。

試す前に、新しいシートで次のコードを実行し、セルA1〜A10000に1〜10000の数値を入力しておきましょう。

【テストデータを作るコード】

Sub MakeTestData()
    ' A1〜A10000に1〜10000の数値を入力する
    Range("A1:A10000").Formula = "=ROW()"
    Range("A1:A10000").Value = Range("A1:A10000").Value   ' 数式を値に変換
End Sub

セルを1つずつ処理する方法

まずは配列を使わずに、セルを1つずつ読み書きする書き方です。

【サンプルコード】

Sub DoubleValuesCellByCell()
    Dim i As Long
    Dim startTime As Double

    startTime = Timer

    For i = 1 To 10000
        ' ループのたびに、セルの読み込みと書き込みが1回ずつ発生する
        Cells(i, 2).Value = Cells(i, 1).Value * 2
    Next i

    MsgBox "処理時間:" & Format(Timer - startTime, "0.000") & "秒"
End Sub

コードは短くて分かりやすいですが、ループのたびにセルの読み込みと書き込みが発生するため、シートへのアクセスは合計2万回になります。

配列にまとめて読み込んで処理する方法

次に、同じ処理を配列で書いた例です。

【サンプルコード】

Sub DoubleValuesWithArray()
    Dim data As Variant
    Dim i As Long
    Dim startTime As Double

    startTime = Timer

    ' ①セル範囲をまとめて配列に読み込む(シートへのアクセス1回)
    data = Range("A1:A10000").Value

    ' ②配列の中で計算する(シートにはアクセスしない)
    For i = LBound(data, 1) To UBound(data, 1)
        data(i, 1) = data(i, 1) * 2
    Next i

    ' ③配列をまとめてセル範囲に書き込む(シートへのアクセス1回)
    Range("B1:B10000").Value = data

    MsgBox "処理時間:" & Format(Timer - startTime, "0.000") & "秒"
End Sub

処理の流れは「①まとめて読み込む → ②配列の中で計算する → ③まとめて書き込む」の3ステップです。ループの回数は同じ1万回ですが、ループの中ではシートにアクセスしていないため、シートへのアクセスは読み込み1回・書き込み1回の合計2回で済みます。

配列を使うと速くなる理由

2つのコードの違いを整理すると、次のようになります。

比較項目セルを1つずつ処理配列で一括処理
セルの読み込み10,000回1回
セルへの書き込み10,000回1回
計算する場所ループのたびにシートとやり取りVBAのメモリ上(配列)
実測した処理時間(参考)約1.41秒(5回の平均)約0.04秒(5回の平均)

VBAからセルを読み書きする処理は、VBAの変数(配列)を読み書きする処理に比べて時間がかかります。1回あたりの差はわずかでも、1万回・10万回と繰り返すと大きな差になります。配列を使う方法は、時間のかかる「シートへのアクセス」の回数そのものを減らすため、データが多いほど効果が大きくなります。

表の処理時間は、次の条件で計測した参考値です。パソコンの性能やExcelの設定、シートの状態によって数値は変わります。

  • 検証環境:Windows 11 Home、Microsoft 365版Excel(64ビット、バージョン16.0.20430.20092)、AMD Ryzen 5(Surface向けモデル)、メモリ約8GB
  • データ件数:A1〜A10000の数値1万件(上記のMakeTestDataで作成)
  • 処理内容:A列の値を2倍にしてB列に書き込む
  • 計測方法:上記2つのマクロ内でTimer関数を使って計測。B列を空にしてからそれぞれ5回ずつ実行した平均値(Excelの画面を表示した状態で実行。画面更新の停止などの高速化設定はなし)

なお、Application.ScreenUpdating = False(画面更新の停止)でもセルを1つずつ処理するコードは速くなりますが、シートへのアクセス回数は変わりません。大量のデータを扱う場合は、まず配列でアクセス回数を減らすことを考えましょう。

動的配列で要素数を変更する

ここでは、動的配列の要素数を決めたり変更したりする基本(ReDim・ReDim Preserve)を解説します。要素を1件ずつ追加していく方法など、動的配列の詳しい使い方は、この章の最後で紹介する記事で解説しています。

要素数を決め直す

動的配列の要素数を決めたり変更したりするには、ReDimステートメントを使います。

【基本構文】

ReDim 配列名(最後の番号)
ReDim 配列名(最初の番号 To 最後の番号)

ReDimを実行すると、それまで入っていた値はすべて初期値に戻ります。値を残したい場合は、次に紹介するPreserveを付けます。

また、ReDimでAs データ型を付けてデータ型を変更できるのは、Dim v As VariantのようにVariant型の変数に配列を入れている場合だけです。Dim arr() As Longのように型を指定して宣言した動的配列は、ReDimで別の型に変えることはできません(コンパイルエラー「配列要素のデータ型を変更することはできません。」になります)。

値を残したまま要素を追加する

ReDimにPreserveキーワードを付けると、すでに入っている値を残したまま要素数を変更できます。

【基本構文】

ReDim Preserve 配列名(最後の番号)

PreserveありとPreserveなしの違いを、次のコードで確認してみましょう。

【サンプルコード】

Sub ReDimAndPreserve()
    Dim scores() As Long

    ReDim scores(1 To 2)
    scores(1) = 80
    scores(2) = 65

    ' Preserveあり:値を残したまま3つに増やす
    ReDim Preserve scores(1 To 3)
    scores(3) = 92
    MsgBox "Preserveあり:" & scores(1) & ", " & scores(2) & ", " & scores(3)

    ' Preserveなし:値はすべて初期値(Long型なので0)に戻る
    ReDim scores(1 To 3)
    MsgBox "Preserveなし:" & scores(1) & ", " & scores(2) & ", " & scores(3)
End Sub

【実行結果(メッセージボックスの表示)】

Preserveあり:80, 65, 92
Preserveなし:0, 0, 0

Preserveで要素数を減らした場合は、はみ出した要素の値が失われます。また、Preserveを付けたときは最初の番号(下限)を変更できない点にも注意しましょう。

ループの中でReDim Preserveを使うと、1件ずつ要素を追加していくこともできます。ただし、ReDim Preserveは実行するたびに配列のサイズを変更する処理が行われるため、件数やデータ型によっては時間がかかります(Variant型の配列に100万件を1件ずつ追加したところ、約20秒かかりました)。件数の上限が分かっている場合は、先に大きめのサイズで確保しておくのがおすすめです。動的配列への要素の追加は、以下の記事でさらに詳しく解説しています。

▼こちらもチェック▼

二次元配列で要素数を変えるときの注意点

二次元配列でReDim Preserveを使う場合、変更できるのは最後の次元(二次元配列なら列のほう)のサイズだけです。1つ目の次元(行)のサイズを変えようとすると、実行時エラーになります。次元の数(一次元 → 二次元など)を変えることもできません。

【エラーになるコード】

Sub ErrorPreserve2D()
    Dim data() As Variant

    ReDim data(1 To 3, 1 To 2)
    ReDim Preserve data(1 To 4, 1 To 2)   ' ← 1つ目の次元(行)は変更できないのでエラー
End Sub

このコードを実行すると、「実行時エラー ‘9’:インデックスが有効範囲にありません。」というエラーが表示されます。

セル範囲に書き出す表データは「行」を増やしたい場面が多いため、この制約はよく問題になります。実務では、行数の最大値で先に配列を確保しておき、実際に使った行数だけをシートに書き込む方法が簡単です。

次のコードは、A2〜B6に入った「名前」と「点数」のうち、70点以上の行だけをD列・E列に書き出します。

【サンプルコード】

Sub ExtractPassedRows()
    Dim data As Variant
    Dim result() As Variant
    Dim i As Long
    Dim count As Long

    data = Range("A2:B6").Value

    ' 全行が条件に合う可能性があるので、元データと同じ行数で確保しておく
    ReDim result(1 To UBound(data, 1), 1 To 2)

    For i = LBound(data, 1) To UBound(data, 1)
        If data(i, 2) >= 70 Then
            count = count + 1
            result(count, 1) = data(i, 1)
            result(count, 2) = data(i, 2)
        End If
    Next i

    ' 実際に使った行数(count)だけをD2から書き込む
    If count > 0 Then
        Range("D2").Resize(count, 2).Value = result
    End If
End Sub

【実行前のシート(A1〜B6)】

行A列B列
1名前点数
2佐藤85
3鈴木62
4高橋74
5田中58
6伊藤91

【実行結果(D2〜E4に書き込まれる値)】

D2:佐藤 E2:85
D3:高橋 E3:74
D4:伊藤 E4:91

配列resultは5行分ありますが、Resize(count, 2)で書き込み先を3行に絞っているため、使っていない行はシートに書き込まれません。

配列のよく使う操作

ここからは、配列のコピー・並べ替え・検索・削除・結合・比較など、応用的な操作を紹介します。

配列をコピーする

配列を別の変数にコピーする場合は、動的配列またはVariant型の変数に代入します。

【サンプルコード】

Sub sampleCopy()
    Dim arr1() As Variant, arr2 As Variant
    Dim st As Variant
    Dim msg As String

    arr1 = Array("りんご", "みかん", "ぶどう")
    arr2 = arr1

    For Each st In arr2
        msg = msg & st & " "
    Next st

    MsgBox msg
End Sub

【実行結果】

代入でコピーした配列は、元の配列とは別物になります。arr2(0)の値を書き換えても、arr1(0)は変わりません。

なお、Dim arr2(2) As Variantのような静的配列には、配列をまとめて代入できません(コンパイルエラー「配列には割り当てられません。」になります)。コピー先は括弧の中を空にした動的配列か、Variant型の変数にしましょう。

配列を並べ替える

VBAには、配列そのものを並べ替える標準の関数がありません。少ない件数であれば、ループを使って並べ替えられます。

【サンプルコード】

Sub SortArray()
    Dim nums As Variant
    Dim i As Long, j As Long
    Dim tmp As Variant

    nums = Array(5, 3, 8, 1, 4)

    ' i番目の要素と、それより後ろの要素を比べて、小さいほうを前に移す
    For i = LBound(nums) To UBound(nums) - 1
        For j = i + 1 To UBound(nums)
            If nums(i) > nums(j) Then
                tmp = nums(i)
                nums(i) = nums(j)
                nums(j) = tmp
            End If
        Next j
    Next i

    MsgBox Join(nums, ", ")
End Sub

【実行結果(メッセージボックスの表示)】

1, 3, 4, 5, 8

降順にしたい場合は、nums(i) > nums(j)をnums(i) < nums(j)に変えます。

この方法は要素数が増えると比較の回数が急激に増えるため、数千件以上のデータには向きません。大量のデータや表全体を並べ替えたい場合は、配列の内容をいったんセルに書き出し、Sortメソッドで並べ替えてから、再び配列に読み込む方法がおすすめです。

【Sortメソッドの構文例】

Range("範囲").Sort Key1:=Range("キー"), Order1:=xlAscending

Key1はソートの基準となる列、Order1は並び替えの順序(昇順はxlAscending、降順はxlDescending)を指定します。Sortメソッドの詳しい使い方は、以下の記事をご覧ください。

▼こちらもチェック▼

配列から要素を検索する

配列の中から、指定した文字列を含む要素を取り出したい場合は、Filter関数を使います。

【基本構文】

Filter(sourcearray, match[, include[, compare]])

sourcearrayには検索対象の一次元配列、matchには検索する文字列を指定します。

includeは、検索する文字列を含む要素を取り出すか(True)、含まない要素を取り出すか(False)を指定します。

compareは、文字列を比較するモードを指定します。「vbBinaryCompare」を指定した場合は大文字と小文字を区別して検索し、「vbTextCompare」を指定した場合は区別せずに検索します。

[ ]内の2つは省略可能です。

以下のサンプルコードで、実際の動きを確認してみましょう。

【サンプルコード】

Sub sampleFilter()
    Dim arr() As Variant, fArray As Variant
    Dim st As Variant
    Dim msg As String

    arr = Array("Windows10", "windows11", "macOS", "WindowsXP")
    fArray = Filter(arr, "Windows")

    For Each st In fArray
        msg = msg & st & " "
    Next st

    MsgBox msg
End Sub

【実行結果】

includeは省略するとデフォルトでTrueとなり、compareを省略した場合は大文字と小文字が区別されます(Option Compareステートメントを書いていない場合)。そのため、このサンプルでは小文字で始まる「windows11」は取り出されていません。

Filter関数には、次の点にも注意しましょう。

  • 部分一致で検索するため、「Win」で検索しても「Windows10」が取り出される
  • 結果の配列は番号が0から始まる
  • 一致する要素がない場合は要素数0の配列が返り、UBoundが「-1」になる

値が完全に一致する要素の位置を知りたい場合は、ループで1つずつ比べます。

【サンプルコード】

Sub FindExactMatch()
    Dim fruits As Variant
    Dim i As Long
    Dim foundIndex As Long

    fruits = Array("りんご", "みかん", "ぶどう")
    foundIndex = -1   ' 見つからなかったときの目印

    For i = LBound(fruits) To UBound(fruits)
        If fruits(i) = "みかん" Then
            foundIndex = i
            Exit For   ' 見つかったらループを抜ける
        End If
    Next i

    If foundIndex = -1 Then
        MsgBox "見つかりませんでした"
    Else
        MsgBox "みかんは fruits(" & foundIndex & ") にあります"
    End If
End Sub

【実行結果(メッセージボックスの表示)】

みかんは fruits(1) にあります

何度も検索を繰り返す場合や、重複をチェックしたい場合は、配列よりもDictionaryのほうが向いています(「配列・Collection・Dictionaryの使い分け」を参照してください)。

配列・Collection・Dictionaryの使い分け

配列の要素を削除する

VBAには、配列の一部の要素を削除する機能がありません。代わりに、ReDim Preserveを応用して削除する方法を紹介します。

【サンプルコード】

Sub sampleRemove()
    Dim arr() As Variant
    Dim i As Long
    Dim st As Variant
    Dim msg As String

    arr = Array(0, 1, 2, 3, 4)

    ' 3番目の要素(arr(2))を削除する
    For i = 2 To UBound(arr) - 1
        arr(i) = arr(i + 1)
    Next i

    ReDim Preserve arr(UBound(arr) - 1)

    For Each st In arr
        msg = msg & st & " "
    Next st

    MsgBox msg
End Sub

【実行結果】

ポイントは、削除したい要素からループを開始して、1つ後ろの値を前に詰めて上書きしていることです。

値を前に詰めると、最後の要素が不要になります。そこでReDim Preserveで要素数を1つ減らし、最後の要素を切り捨てることで、必要な値だけを配列に残しています。

配列の要素を1つの文字列に結合する

配列内の要素をつなげて1つの文字列にしたい場合は、Join関数を使うと便利です。

【基本構文】

Join(配列名, [区切り文字])

区切り文字を指定すると、要素同士の間にその文字列を入れて結合します。省略した場合は半角スペースが入り、""(長さ0の文字列)を指定すると区切り文字なしで結合します。対象にできるのは一次元配列です。

【サンプルコード】

Sub sampleJoin()
    Dim parts As Variant

    parts = Array("2026", "10", "07")

    MsgBox Join(parts, "/")
End Sub

【実行結果(メッセージボックスの表示)】

2026/10/07

複数の配列を連結する

別々の配列を1つに連結する関数はVBAには用意されていないので、代わりにSplit関数とJoin関数を組み合わせて使う方法を紹介します。

まず初めに、Join関数を使って、それぞれの配列内の要素を結合します。

結合が完了したら、出来上がった文字列同士を「&」記号を使ってさらに結合し、一つの文字列にまとめましょう。その際、関数で指定した区切り文字を、それぞれの文字列の間に挟むのがポイントです。

次に、結合した文字列をSplit関数で分割して、配列の各要素に格納します。これにより、複数の配列を1つに連結させることができます。

実際の流れをサンプルコードで確認してみましょう。

【サンプルコード】

Sub sampleJoinSplit()
    Dim arr1() As Variant, arr2() As Variant, arr3 As Variant
    Dim st As Variant
    Dim msg As String

    arr1 = Array(1, 2)
    arr2 = Array(3, 4, 5)
    arr3 = Split(Join(arr1) & " " & Join(arr2))

    For Each st In arr3
        msg = msg & st & ", "
    Next st

    MsgBox msg
End Sub

【実行結果】

この方法は手軽ですが、いったん文字列にするため、連結後の要素はすべて文字列(String型)になります。また、要素の中に区切り文字(ここでは半角スペース)が含まれていると、正しく分割されません。

数値などのデータ型をそのまま残したい場合は、連結後の要素数で配列を用意し、ループで1つずつコピーします。

【サンプルコード】

Sub ConcatArrays()
    Dim arr1 As Variant, arr2 As Variant
    Dim result() As Variant
    Dim i As Long, n As Long
    Dim size1 As Long, size2 As Long

    arr1 = Array(1, 2)
    arr2 = Array(3, 4, 5)

    ' それぞれの要素数を求めて、合計の要素数で配列を用意する
    size1 = UBound(arr1) - LBound(arr1) + 1
    size2 = UBound(arr2) - LBound(arr2) + 1
    ReDim result(0 To size1 + size2 - 1)

    ' arr1、arr2の順に、要素を1つずつコピーする
    For i = LBound(arr1) To UBound(arr1)
        result(n) = arr1(i)
        n = n + 1
    Next i
    For i = LBound(arr2) To UBound(arr2)
        result(n) = arr2(i)
        n = n + 1
    Next i

    MsgBox Join(result, ", ") & vbCrLf & "result(4)のデータ型:" & TypeName(result(4))
End Sub

【実行結果(メッセージボックスの表示)】

1, 2, 3, 4, 5
result(4)のデータ型:Integer

配列同士を比較する

配列同士を比較する際、ループを使って要素ごとに比較を行う方法がありますが、処理が複雑になりがちです。

一次元配列で、同一か否かだけを判断したい場合は、Join関数を使って要素を結合した文字列を比較すると簡潔に記述できます。

【サンプルコード】

Sub sampleCompare()
    Dim arr1() As Variant, arr2() As Variant

    arr1 = Array("11", "21", "322", "27", "43")
    arr2 = Array("11", "21", "321", "27", "43")

    If Join(arr1) = Join(arr2) Then
        MsgBox "同じ配列です"
    Else
        MsgBox "違う配列です"
    End If
End Sub

【実行結果】

ただし、この方法では要素の区切りが分からなくなるため、たとえばArray("a b", "c")とArray("a", "b c")はどちらも「a b c」となり、同じ配列と判定されてしまいます。要素に半角スペースが含まれる可能性がある場合は、Join(arr1, vbTab)のようにデータに含まれない区切り文字を指定するか、ループで1つずつ比較しましょう。

定数のように使う配列を作る

VBAでは、配列を定数(Const)として宣言することはできません。定数として宣言できるのは、数値・文字列・真偽値など、コンパイル時に決まる1つの値だけだからです。たとえばConst NUMS = Array(1, 2)と書くと、コンパイルエラー「定数式が必要です。」になります。

そこで、厳密な定数とはいきませんが、区切り文字を入れた文字列を定数にしておき、使うときにSplit関数で分割して配列にする方法を紹介します。

【サンプルコード】

Const NUM_LIST As String = "1,2,3,4,5"

Sub sampleConst()
    Dim arr As Variant
    Dim st As Variant
    Dim msg As String

    arr = Split(NUM_LIST, ",")

    For Each st In arr
        msg = msg & st & " "
    Next st

    MsgBox msg
End Sub

【実行結果】

Const文は、モジュールの先頭(Subの外)に書きます。Split関数が返す配列は、番号が0から始まる文字列の配列です。

配列を関数の引数・戻り値にする

配列を関数(Function)やSubの引数に指定する場合は、引数名の後に括弧を付けます。

【基本構文】

Sub プロシージャ名(配列名() As データ型)

戻り値に配列を指定する場合には、戻り値のデータ型の後に括弧を付けます。

【基本構文】

Function プロシージャ名(配列名() As データ型) As データ型()

次のコードでは、1〜nの2乗を配列で返す関数MakeSquaresと、配列の合計を返す関数SumArrayを作り、組み合わせて使っています。

【サンプルコード】

' 1〜nの2乗を入れた配列を返す
Function MakeSquares(n As Long) As Long()
    Dim result() As Long
    Dim i As Long

    ReDim result(1 To n)
    For i = 1 To n
        result(i) = i * i
    Next i

    MakeSquares = result
End Function

' 配列の合計を返す
Function SumArray(nums() As Long) As Long
    Dim i As Long

    For i = LBound(nums) To UBound(nums)
        SumArray = SumArray + nums(i)
    Next i
End Function

Sub CallArrayFunctions()
    Dim squares() As Long

    squares = MakeSquares(3)   ' 1, 4, 9 が入る
    MsgBox "合計:" & SumArray(squares)
End Sub

【実行結果(メッセージボックスの表示)】

合計:14

引数をnums() As Longのように型付きの配列にした場合は、同じ型の配列(ここではLong型の配列)を渡す必要があります。また、配列の引数は参照渡し(ByRef)にする必要があり、ByVal nums() As Longと書くとエラーになってマクロを実行できません。Functionの使い方は、以下の記事で詳しく解説しています。

▼こちらもチェック▼

配列でよくあるエラーと対処法

「インデックスが有効範囲にありません」の主な原因

配列で最もよく見かけるのが、「実行時エラー ‘9’:インデックスが有効範囲にありません。」です。配列に存在しない番号(添字)を指定したときに発生します。主な原因は次のとおりです。

原因よくある例
宣言したサイズを超えて添字を指定しているDim fruits(2)なのにfruits(3)に代入する
配列の開始番号を勘違いしているセル範囲から作った配列(1始まり)にdata(0, 1)でアクセスする
ループの範囲を固定値で書いている要素数が変わったのにFor i = 0 To 3のまま
要素数を確保していない・確保したサイズを勘違いしているReDimの前に動的配列へ代入する/ReDim names(3)を「1〜3」だと思い込む
二次元配列の行と列を取り違えている5行×2列の配列にdata(1, 5)でアクセスする
二次元配列で1つ目の次元をReDim Preserveしている行数を増やそうとしてReDim Preserve data(1 To 4, 1 To 2)と書く(動的配列で要素数を変更するを参照)

エラーが出たら、[デバッグ]ボタンを押して黄色く表示された行を確認し、イミディエイトウィンドウで? LBound(配列名)・? UBound(配列名)と入力して、配列の番号の範囲を調べてみましょう。以下で、原因ごとに「エラーになるコード → 原因 → 修正版」を紹介します。

宣言したサイズを超えて添字を指定している

【エラーになるコード】

Sub ErrorOutOfRange()
    Dim fruits(2) As String

    fruits(0) = "りんご"
    fruits(1) = "みかん"
    fruits(2) = "バナナ"
    fruits(3) = "ぶどう"   ' ← ここでエラー
End Sub

原因:Dim fruits(2)で用意されるのはfruits(0)〜fruits(2)の3つだけです。4つ目のfruits(3)は存在しません。

【修正版】

Sub FixOutOfRange()
    Dim fruits(3) As String   ' 4つ入れたいので、最後の番号を3にする

    fruits(0) = "りんご"
    fruits(1) = "みかん"
    fruits(2) = "バナナ"
    fruits(3) = "ぶどう"
End Sub

入れる値の数があとから増える可能性がある場合は、動的配列にしてReDim Preserveで要素を追加します。

配列の開始番号を勘違いしている

【エラーになるコード】

Sub ErrorStartIndex()
    Dim data As Variant

    data = Range("A1:A3").Value
    MsgBox data(0, 1)   ' ← ここでエラー
End Sub

原因:Array関数やSplit関数で作った配列は0から始まりますが、セル範囲から読み込んだ配列は1から始まります。data(0, 1)は存在しません。

【修正版】

Sub FixStartIndex()
    Dim data As Variant

    data = Range("A1:A3").Value
    MsgBox data(LBound(data, 1), 1)   ' 先頭の行は LBound で取得する(ここでは1)
End Sub

ループの範囲を固定値で書いている

【エラーになるコード】

Sub ErrorFixedLoop()
    Dim scores As Variant
    Dim i As Long
    Dim total As Long

    scores = Array(80, 65, 92)

    For i = 0 To 3                   ' ← 要素は3つ(0〜2)なのに3まで回している
        total = total + scores(i)    ' ← i = 3 のときにエラー
    Next i
End Sub

原因:以前は4つあった値を3つに減らしたのに、ループの範囲を直し忘れているケースです。

【修正版】

Sub FixFixedLoop()
    Dim scores As Variant
    Dim i As Long
    Dim total As Long

    scores = Array(80, 65, 92)

    For i = LBound(scores) To UBound(scores)   ' 配列の範囲に自動で合わせる
        total = total + scores(i)
    Next i

    MsgBox "合計:" & total
End Sub

要素数を確保していない・確保したサイズを勘違いしている

【エラーになるコード①:要素数を確保していない】

Sub ErrorNoReDim()
    Dim names() As String

    names(0) = "田中"   ' ← ここでエラー
End Sub

原因:Dim names() As Stringと宣言しただけの動的配列には、まだ要素が1つもありません。

【エラーになるコード②:確保したサイズを勘違いしている】

Sub ErrorReDimSize()
    Dim names() As String
    Dim i As Long

    ReDim names(3)       ' names(0)〜names(3) の4つが用意される

    For i = 1 To 4       ' ← 「4つだから1〜4」と考えてしまった
        names(i) = "社員" & i   ' ← i = 4 のときにエラー
    Next i
End Sub

原因:ReDim names(3)の「3」は要素数ではなく最後の番号です。番号は0〜3なので、names(4)は存在しません。

【修正版】

Sub FixReDimSize()
    Dim names() As String
    Dim i As Long

    ReDim names(1 To 4)   ' 1〜4の4つを用意する(ReDimしてから代入する)

    For i = LBound(names) To UBound(names)
        names(i) = "社員" & i
    Next i

    MsgBox Join(names, ", ")
End Sub

「1番〜4番」のように要素数で考えたい場合は、1 To 4と最初の番号も書いておくと勘違いを防げます。

二次元配列の行と列を取り違えている

【エラーになるコード】

Sub ErrorRowColumn()
    Dim data As Variant

    data = Range("A1:B5").Value   ' 5行×2列の配列
    MsgBox data(1, 5)             ' ← 「1行目の5列目」になってしまいエラー
End Sub

原因:二次元配列は(行, 列)の順に番号を書きます。「5行目の1列目(セルA5)」のつもりで番号を逆に書くと、存在しない5列目を指定したことになります。

【修正版】

Sub FixRowColumn()
    Dim data As Variant
    Dim r As Long, c As Long
    Dim cellCount As Long

    data = Range("A1:B5").Value

    MsgBox data(5, 1)   ' 5行目・1列目(セルA5の値)

    ' ループするときも「1つ目 = 行」「2つ目 = 列」で範囲を取る
    For r = LBound(data, 1) To UBound(data, 1)
        For c = LBound(data, 2) To UBound(data, 2)
            cellCount = cellCount + 1
        Next c
    Next r

    MsgBox "要素数:" & cellCount   ' 5行×2列なので10
End Sub

1つのセルを配列として扱おうとしている

「インデックスが有効範囲にありません」とは別のエラーですが、セル範囲を配列に読み込むコードでは次のエラーもよく起こります。

【エラーになるコード】

Sub ErrorSingleCell()
    Dim data As Variant
    Dim lastRow As Long

    lastRow = Cells(Rows.Count, "A").End(xlUp).Row   ' データがA1だけだと lastRow = 1
    data = Range("A1:A" & lastRow).Value           ' 「A1:A1」= セル1つなので配列にならない
    MsgBox data(1, 1)                               ' ← ここでエラー
End Sub

原因:セル範囲が1つのセルだけの場合、Valueは配列ではなく単なる値を返します。配列ではない変数に番号を付けてアクセスしたため、「実行時エラー ’13’:型が一致しません。」になります。

【修正版】

Sub FixSingleCell()
    Dim data As Variant
    Dim lastRow As Long

    lastRow = Cells(Rows.Count, "A").End(xlUp).Row
    data = Range("A1:A" & lastRow).Value

    ' セル1つだけのときは、1行×1列の配列を作り直す
    If Not IsArray(data) Then
        Dim tmp As Variant
        tmp = data
        ReDim data(1 To 1, 1 To 1)
        data(1, 1) = tmp
    End If

    MsgBox data(1, 1)
End Sub

IsArray関数は、変数に配列が入っているかどうかをTrue/Falseで返します。データ型が原因のエラー全般については、以下の記事も参考にしてください。

▼こちらもチェック▼

配列・Collection・Dictionaryの使い分け

VBAで複数の値をまとめて扱う方法は、配列のほかにCollection(コレクション)とDictionary(ディクショナリ)があります。それぞれ得意な処理が異なるため、用途に合わせて使い分けましょう。

比較項目配列CollectionDictionary
向いている用途大量のデータを順番に処理する/セル範囲とまとめてやり取りする件数が分からないデータを、順番に追加・削除しながら扱うキーと値をセットで管理する/重複チェックや検索を何度も行う
要素の指定方法番号(添字)番号(1から始まる)または キーキー
要素の追加・削除ReDim Preserveなどで工夫が必要Add・Removeメソッドで簡単にできるAdd・Removeメソッドで簡単にできる
キーの存在確認ループで探す確認用のメソッドはないExistsメソッドで確認できる
セル範囲との一括やり取りRange.Valueで直接できるできない直接はできない(Keys・Itemsメソッドで配列として取り出せる)
使うための準備不要(VBAの標準機能)不要(New Collectionで作成)Microsoft Scripting Runtimeのオブジェクト(CreateObject("Scripting.Dictionary")で作成するか、参照設定を追加する)

迷ったときは、次のように考えるとよいでしょう。

  • セル範囲を読み込んで加工・集計したい → 配列
  • 何件になるか分からないデータを順番にため込みたい → Collection
  • 「商品コード → 商品名」のように、キーで値を引きたい・重複を除きたい → Dictionary

実務では、「セル範囲を配列で読み込み、Dictionaryで重複を除いて、結果を配列に戻してシートに書き込む」のように組み合わせて使うこともよくあります。Dictionaryの使い方は、以下の記事で詳しく解説しています。

▼こちらもチェック▼

VBAの配列に関するよくある質問

1列のセル範囲を一次元配列に変換するには?

セル範囲を読み込んだ配列は、1列だけでも二次元配列になるため、一次元配列を前提とするJoin関数やFilter関数にそのまま渡すことはできません。WorksheetFunction.Transposeで行と列を入れ替えると、番号が1から始まる一次元配列に変換できます。

A1〜A3に「りんご」「みかん」「バナナ」が入力されている場合の例です。

Sub ColumnToList()
    Dim data As Variant
    Dim list As Variant

    data = Range("A1:A3").Value                     ' 3行×1列の二次元配列
    list = WorksheetFunction.Transpose(data)        ' 1〜3の一次元配列に変換

    MsgBox Join(list, "、") & vbCrLf & "最初の番号:" & LBound(list) & " 最後の番号:" & UBound(list)
End Sub

【実行結果(メッセージボックスの表示)】

りんご、みかん、バナナ
最初の番号:1 最後の番号:3

配列の番号は0から始まる?1から始まる?

配列の作り方によって異なります。

配列の作り方最初の番号
Dim arr(2)のように最後の番号だけ指定0(Option Base 1を書いた場合は1)
Dim arr(1 To 3)のように最初の番号も指定指定した番号(この例では1)
Array関数0(Option Base 1を書いた場合は1)
Split関数・Filter関数の結果0
セル範囲(Range.Value)から読み込み1

作り方ごとに覚えるより、ループや要素数の計算ではLBound・UBoundを使うと決めておけば、開始番号の違いを気にせずに済みます。

配列のすべての要素に同じ初期値を入れるには?

ReDimやDimで要素数を決めた直後の配列には、データ型ごとの初期値(数値なら0、文字列なら長さ0の文字列)が自動で入ります。それ以外の値で埋めたい場合は、ループで代入します。

Sub FillArray()
    Dim stock() As Long
    Dim i As Long

    ReDim stock(1 To 5)        ' この時点ではすべての要素が0

    For i = LBound(stock) To UBound(stock)
        stock(i) = 100         ' すべての要素に100を入れる
    Next i

    MsgBox "stock(1)=" & stock(1) & " stock(5)=" & stock(5)
End Sub

一次元配列をシートの縦方向に書き込むには?

一次元配列をセル範囲に代入すると、横方向(1行)に書き込まれます。縦方向(1列)に書き込みたい場合は、WorksheetFunction.Transposeで行と列を入れ替えてから代入します。

Sub WriteVertical()
    Dim fruits As Variant

    fruits = Array("りんご", "みかん", "バナナ")

    Range("A1:C1").Value = fruits                               ' 横方向:A1〜C1
    Range("E1:E3").Value = WorksheetFunction.Transpose(fruits)  ' 縦方向:E1〜E3
End Sub

件数が多い場合や、値を確実にそのまま書き込みたい場合は、最初からReDim data(1 To 件数, 1 To 1)のような「n行×1列」の二次元配列を作って代入する方法が確実です。

動的配列が空かどうかを判定するには?

ReDimする前の動的配列にUBoundを使うと「インデックスが有効範囲にありません」のエラーになります。この性質を利用して、エラーが起きるかどうかで判定する関数を作るのが一般的です。

' 配列に要素が1つ以上あれば True を返す
Function HasElements(arr As Variant) As Boolean
    On Error GoTo NoElements
    HasElements = (UBound(arr) >= LBound(arr))   ' Array() で作った空の配列は UBound が -1 なので False
    Exit Function
NoElements:
    HasElements = False                         ' ReDim前・Erase後の配列は UBound がエラーになる
End Function

Sub CheckEmptyArray()
    Dim names() As String
    Dim msg As String

    msg = "ReDim前:" & HasElements(names) & vbCrLf
    ReDim names(0)
    msg = msg & "ReDim後:" & HasElements(names)

    MsgBox msg
End Sub

【実行結果(メッセージボックスの表示)】

ReDim前:False
ReDim後:True

エラー処理(On Error)の書き方については、以下の記事で詳しく解説しています。

▼こちらもチェック▼

まとめ

VBAの配列は、複数の値を1つの名前と番号でまとめて扱う仕組みです。この記事のポイントを振り返っておきましょう。

  • Dim 配列名(2)の「2」は最後の番号で、要素は0〜2の3つになる
  • ループや要素数の計算では、固定値ではなくLBound・UBoundを使う
  • セル範囲をRange.Valueで読み込むと、1から始まる(行, 列)の二次元配列になる
  • 「まとめて読み込む → 配列の中で処理する → まとめて書き込む」でシートへのアクセスを減らすと、処理が速くなる
  • ReDim Preserveは値を残したまま要素数を変えられるが、二次元配列では最後の次元しか変更できない
  • キーで検索したい・重複を除きたい場合はDictionaryも検討する

まずは「まずは配列を動かしてみよう」のコードを実行し、慣れてきたら、普段の業務で使っているマクロのセル処理を配列に置き換えてみてください。

VBAの配列の仕様は、Microsoftの公式ドキュメントでも確認できます。

関連記事

VBAの勉強方法は?

書籍やインターネットで学習する方法があります。昨今では、YouTubeなどの動画サイトやエンジニアのコミュニティサイトなども充実していて多くの情報が手に入ります。
そして、より効率的に知識・スキルを習得するには、知識をつけながら実際に手を動かしてみるなど、インプットとアウトプットを繰り返していくことが重要です。特に独学の場合は、有識者に質問ができたりフィードバックをもらえるような環境があると、理解度が深まるでしょう。

ただ、VBAに限らず、ITスキルを身につける際、どうしても課題にぶつかってしまうことはありますよね。特に独学だと、わからない部分をプロに質問できる機会を確保しにくく、モチベーションが続きにくいという側面があります。独学でモチベーションを維持する自信がない人にはプログラミングスクールという手もあります。費用は掛かりますが、その分スキルを身につけやすいです。しっかりと知識・スキルを習得して実践に活かしたいという人はプログラミングスクールがおすすめです。

プログラミングスクールならテックマニアがおすすめ!

ITスキル需要の高まりとともにプログラミングスクールも増えました。しかし、どのスクールに通うべきか迷ってしまう人もいるでしょう。そんな方にはテックマニアをおすすめします!これまで多くのITエンジニアを育成・輩出してきたテックマニアでもプログラミングスクールを開講しています。

<テックマニアの特徴>
・たしかな育成実績と親身な教育 ~セカンドキャリアを全力支援~
・講師が現役エンジニア ~“本当”の開発ノウハウを直に学べる~
・専属講師が学習を徹底サポート ~「わからない」を徹底解決~
・実務ベースでスキルを習得 ~実践的な凝縮カリキュラム~

このような特徴を持つテックマニアはITエンジニアのスタートラインとして最適です。
話を聞きたい・詳しく知りたいという方はこちらからお気軽にお問い合わせください。

     

VBAカテゴリの最新記事