VBAのDictionaryでExistsがTrueになる原因とは?Variant配列の値で存在確認するときの注意点

Visual Basic

VBAでScripting.DictionaryのExistsメソッドを使っているにもかかわらず、未登録のキーが登録されたように見えたり、Existsが予想外にTrueを返したりするケースがあります。特にWorksheetから取得したVariant型の二次元配列を扱う場合は、データ型や暗黙的な変換が影響することがあります。この記事では、DictionaryのExistsとVariant配列の関係について解説します。

DictionaryのExistsは本来キーを追加しない

Scripting.DictionaryのExistsメソッドは、指定したキーが存在するかどうかを確認するだけのメソッドです。

そのため、正常な動作であればExistsを実行しただけでキーが追加されることはありません。

If dic.Exists("ABC") Then
    MsgBox "存在します"
End If

上記のコードでは、Existsの実行によってDictionaryの内容が変化することはありません。

Variant型の二次元配列で起こりやすい問題

Excelのセル範囲を配列へ読み込むと、多くの場合はVariant型の二次元配列になります。

このとき、見た目は文字列でも内部的にはEmpty、Null、Error値、またはVariant/Stringなど複数の状態が混在する可能性があります。

例えば次のようなコードでは、aaa(i,1)のデータ型によって予想外の挙動が発生することがあります。

If newDic.Exists(aaa(i,1)) Then
    '処理
End If

特にセルに数式エラーや不可視文字、全角スペースなどが含まれている場合は注意が必要です。

String変数へ代入すると正常になる理由

質問のケースでは、一度String型変数へ代入すると正常に判定できています。

Dim str As String
str = aaa(i,1)
If newDic.Exists(str) Then
    '処理
End If

これは代入時にVBAが明示的な型変換を行い、VariantがStringへ正規化されるためです。

つまり問題の本質は二次元配列ではなく、Variant型の中身やサブタイプにある可能性が高いと言えます。

Dictionaryの既定プロパティとの混同にも注意

Dictionaryでは既定プロパティであるItemを参照した場合、存在しないキーへのアクセスで新規キーが生成されることがあります。

value = dic("ABC")

上記のようなコードは、設定や実装によってはキー追加の原因になることがあります。

そのため、Existsの直前や直後でDictionaryのItemプロパティを参照していないかも確認してみる価値があります。

調査時に確認したいポイント

原因を切り分けるために、以下の情報を出力してみると有効です。

  • TypeName(aaa(i,1))
  • VarType(aaa(i,1))
  • Len(aaa(i,1))
  • AscやAscWによる文字コード確認

これにより、見た目は同じ文字列でも実際には異なるデータが入っているケースを発見できます。

確認項目 目的
TypeName 実際の型を確認
VarType Variantのサブタイプ確認
Len 余分な文字の有無確認
AscW 不可視文字の検出

再発防止のための実装例

実務ではDictionary検索前に明示的な型変換を行うケースが多くあります。

Dim key As String
key = Trim$(CStr(aaa(i,1)))
If newDic.Exists(key) Then
    '処理
End If

DictionaryのExists自体がキーを追加することは基本的にありません。Variantの型情報や別箇所でのItem参照が影響しているケースを疑うべきです。

まとめ

VBAのDictionaryでExistsを実行しただけでキーが追加されたように見える場合、二次元配列そのものが原因というより、Variant型の内部データや暗黙的な型変換が関係している可能性が高いです。

特にExcelから取得した配列データは型が一定ではないため、一度String変数へ代入してからExistsを実行する方法は有効な対策です。また、TypeNameやVarTypeで実際のデータ型を確認することで原因究明につながるでしょう。

コメント

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