【VBA】2次元配列の行数が決まらないときの対処法まとめ

VBAで2次元配列の行数が決まらないときの対処法まとめ。ReDim result(1 To cnt, 1 To 2)をハイライトしたVBEのコード画面 VBA

VBAで2次元配列を使っていると、「格納する件数が事前に分からない」という場面によくぶつかります。以前、ReDim Preserveで2次元配列の行が増やせない原因と対処で解説した通り、ReDim Preserveで変更できるのは2次元配列の最後の次元(列)だけで、行数は変更できません。

この制約に対して、実務でよく使われる対処法は主に3つあります。

  • 最大サイズで確保しておき、あとで正しいサイズに切り出す(スライス法)
  • 先に件数を数えてから、ぴったりのサイズで確保する(2回ループ方式)
  • ユーザー定義型(Type)の1次元配列を使う

このうち1つ目は前回の記事、3つ目はType配列で件数不明のデータをためる方法で紹介済みです。今回は2つ目の「先に件数を数える」方法を中心に、2次元配列の行数が決まらないときの3つの対処法を比較します。

パターン1|最大サイズで確保してから切り出す(スライス法)

対象データの行数を上限として2次元配列を確保しておき、格納が終わったあとに正しいサイズの配列へコピーし直す方法です。1回のループで格納できる代わりに、最後に配列のコピー処理が必要になります。該当件数が0件のときは切り出さず、件数を先に確認してから処理を分ける必要があります。詳しいコードは前回の記事をご覧ください。

パターン2|先に件数を数えてから、ぴったりのサイズで確保する(2回ループ方式)

1回目のループで条件に合う件数だけを数え、その件数ちょうどのサイズで2次元配列を確保してから、2回目のループで値を詰めていく方法です。データを2回読みに行く代わりに、切り出し(配列のコピー)処理が要りません。

' 1回目のループ:条件に合う件数を数える
Dim cnt As Long
cnt = 0
For i = 1 To srcCount
    If 条件 Then
        cnt = cnt + 1
    End If
Next i

' ぴったりのサイズで確保
Dim result() As Variant
ReDim result(1 To cnt, 1 To 列数)

' 2回目のループ:条件に合う行だけ詰める
Dim r As Long
r = 0
For i = 1 To srcCount
    If 条件 Then
        r = r + 1
        result(r, 1) = ...
        result(r, 2) = ...
    End If
Next i
VBAで件数を先に数えてから2次元配列を確保する2回ループ方式のコード全体

スライス法との違いは次の2点です。

  • 切り出し処理が不要:あらかじめぴったりのサイズで確保するため、パターン1のようなコピー処理を書く必要がありません。
  • ループが2回になる:件数を数える分、データを2回走査します。

なお、該当する件数が0件のときは、パターン1と同じ注意が必要です。ReDim result(1 To 0, 1 To 列数)のように上限に0を指定すると、実行時エラー9「インデックスが有効範囲にありません。」になります。これは今回の方法に限った話ではなく、ReDimの一般的な仕様によるものです。実機で確認したところ、確保する行数が0件になる条件では、狙い通りこのエラーが発生しました。0件になり得る処理では、件数が0のときの分岐(配列を確保しない、処理を打ち切るなど)を用意しておく必要があります。

ReDim result(1 To cnt, 1 To 2)の行でVBEのデバッグが止まった画面

コードのSub名などは記事の説明用に付けたもので、再現用の最小コードです。

パターン3|ユーザー定義型(Type)の1次元配列を使う

1行分のデータをTypeで定義し、その1次元配列として扱う方法です。1次元配列なのでReDim Preserveで件数を増やせます。Variantへの直接代入ができないなど独自の制約があるため、詳しくはType配列の記事をご覧ください。

(参考)新しい配列にコピーしながら1行ずつ追記する方法

1行多いサイズで配列を宣言し直し、既存データをコピーしてから新しい行を足す、という方法も可能ではあります。ただし処理の流れが追いにくくなるうえ、件数が多いデータではコピーが繰り返し発生するため、実務で積極的に使う場面は多くありません。「こういう書き方もできる」程度の参考にとどめます。

どれを選ぶか

観点スライス法件数先読み(2回ループ)Type配列
切り出し(コピー)の要否必要不要詰め替え時のみ必要
データの走査回数1回2回1回
0件のときの注意必要必要不要(Preserveで対応可)
Variantへの直接代入可能可能不可(制約あり)

3つとも一長一短があり、「これが正解」というものはありません。処理を1回で済ませたいならスライス法、コードをシンプルに保ちたいなら件数先読み、件数が増減し続ける場面や1次元で完結させたいならType配列、というのが使い分けの目安になります。

まとめ

  • ReDim Preserveは2次元配列の行数を変更できないため、行数が決まらないデータを扱うには工夫が必要
  • 対処法は主に3つ:スライス法/件数先読み(2回ループ)/Type配列
  • 件数先読み方式は切り出し処理が不要な代わりに、データを2回走査する
  • どの方式でも、0件になり得る場合は事前の分岐が必須

コメント

タイトルとURLをコピーしました