【VBA】Type配列で件数不明のデータをためる方法

VBAでユーザー定義型(Type)の1次元配列にReDim Preserveで件数を増やしていくコードのイメージ VBA

前回の記事では、2次元配列でReDim Preserveを使うと行数を増やせない原因と対処法を解説しました。そのとき、行数が読めないデータをためる方法として3つを紹介すると予告しました。今回はそのうちの1つ、ユーザー定義型(Type)の1次元配列を使う方法を紹介します。

困った場面

2次元配列でデータをためようとすると、ReDim Preserveでは行数を増やせません。あらかじめ最大件数を見積もって確保する方法や、先に件数を数えてから確保する方法もありますが、どちらも一手間かかります(3つの方法の比較はこちら)。

そこで使えるのが、1行分の項目をユーザー定義型(Type)にまとめて、1次元配列でためる方法です。1次元配列ならReDim Preserveで件数をそのまま増やせます。

解決コード

Option Explicit
 
Private Type TRecord
    Id As Long
    Name As String
    Amount As Long
End Type
 
Sub AddRecordsWithType()
    Dim buf() As TRecord
    Dim n As Long
    Dim i As Long
    Dim srcCount As Long
 
    srcCount = 5 '実際には元データの件数
    n = -1
 
    For i = 1 To srcCount
        '条件に合うものだけためる想定(ここでは全件ためる例)
        n = n + 1
        ReDim Preserve buf(0 To n)
        buf(n).Id = i
        buf(n).Name = "Item" & i
        buf(n).Amount = i * 100
    Next i
 
    Debug.Print "件数:" & (n + 1)
    Debug.Print buf(0).Name & " / " & buf(n).Name
End Sub

解説

1次元だから ReDim Preserve で増やせる

前回の記事で説明したとおり、ReDim Preserveが変更できるのは配列の最後の次元だけです。2次元配列arr(行, 列)では、最後の次元は列なので、増やしたい行数のほうは変えられません。

ユーザー定義型の配列は1次元です。唯一の次元がそのまま「最後の次元」になるので、ReDim Preserveで件数(2次元配列でいう行数にあたる部分)をそのまま増やせます。1行分の複数の値は、配列の次元を増やすのではなく、Type自体のメンバーとして持たせる形です。

そのまま使う場面と、2次元配列に詰め替える場面

ためたデータをそのままループで処理するだけなら、Type配列のまま使えます。たとえば、1件ずつ条件を見て別の処理に渡す、といった使い方です。

一方、シートに一括で書き出したい場合は、最後に2次元配列(またはVariant配列)に詰め替える必要があります。Type配列をそのままVariant変数に代入しようとすると、コンパイルエラーになるためです。

応用はきくが、使う場面を選ぶ理由

Variantに直接入れられない

標準モジュールで定義したTypeの配列は、Variant変数への代入も、Range.Valueへの直接書き出しも、どちらも次のコンパイルエラーになります。

VBEのコードウィンドウでユーザー定義型の配列をVariant変数に代入するコード
ユーザー定義型の配列をVariantに代入しようとしたときのコンパイルエラーダイアログ

パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。

そのため、シートへ一括で書き出したい場合は、2次元配列(またはVariant配列)に詰め替えるか、セル単位で書き込む必要があります。

構造化のままではソートできない

2次元配列やVariant配列なら、シートに貼ってRange.Sortを使う手があります。しかしType配列は、上記の理由でそのままシートに書き出せないため、この手が使えません。詰め替えてからRange.Sortを使うか、自前でソート処理を書く必要があります。

想定外の値を入れたときの挙動

メンバーに型を指定しているため、想定外の値を入れるとエラーになることがあります。ただし、「型が違えば必ずエラー」というわけではありません。実際に試すと、次のようになりました。

  • 数値に変換できない文字列(例:"abc")をLong型メンバーに代入 → 実行時エラー13「型が一致しません」
  • 数値として解釈できる文字列(例:"123")をLong型メンバーに代入 → エラーにならず、123に変換される
  • 小数を含む数値(例:100.7)をLong型メンバーに代入 → エラーにならず、101に変換される

つまり、暗黙的に変換できる値はエラーにならず、変換できない値だけエラーになります。「型が違うと必ずエラー」ではなく、「暗黙変換できない値を入れるとエラーになる」というほうが正確です。Variantのメンバーを使えばこのエラー自体は避けられますが、型を指定する意味が薄れるうえ、処理も少し重くなります。

まとめ

  • ユーザー定義型(Type)の1次元配列は、ReDim Preserveで件数を増やせる
  • そのまま使うか、2次元配列に詰め替えるかは、後続の処理内容で判断する
  • Variantへの直接代入やRange.Valueへの直接書き出しはコンパイルエラーになるため、シートに書き出すなら詰め替えが必要
  • 構造化のままではソートできない。詰め替えてからRange.Sortを使うか、自前でソートを書く
  • 型不一致は「暗黙変換できない値」を入れたときだけエラーになる。数値に変換できる文字列や小数はエラーにならず変換される

コメント

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