[VBA]Collectionでのキー検索・存在チェック

Collection(コレクション)でキー検索や値の存在チェックを実現VBA

VBAで使用できるCollection(コレクション)オブジェクトですが、メンバー追加時に任意設定で省略可能なキーを設定することができます。

キーを設定することで格納されたメンバーに素早くアプローチすることが出来るようになることと、重複キーを認めないCollection(コレクション)の仕様とでユニークなリストを作成する用途などで活用することができます。

そのようなリストを活用する際には、やはりキーを検索したり、格納したメンバー内に同一のデータが存在するかチェックしたくなるのは当然の使い方です。

VBAのCollection(コレクション)でキー検索・存在チェックを行う方法はどんなやり方で実現すればよいのか?

このあたりで悩まれている方、いらっしゃるのではないでしょうか。

なぜならVBAのCollection(コレクション)はとてもシンプルなプロパティとメソッドしかサポートされていないため、キー検索や存在チェックを一発で実現させる方法が用意されていません。

ではVBAのCollection(コレクション)でキー検索や存在チェックを実現する方法はどんなやり方があるのか、考えてみたいと思います。

スポンサーリンク
スポンサーリンク

Collection(コレクション)のキー検索

Collection(コレクション)のキーは任意設定で省略可能なことがメリットであり、省略可能なゆえにDictionary(連想配列)オブジェクトのようなExistsメソッドがサポートされていないというデメリットもあります。

メリットを最大限に使用していく限りは全く問題ないのですが、使い方次第ではロジックでの対処が求められてしまうケースもあります。

それのひとつがCollection(コレクション)のキー検索です。

ロジック作成の際にキー検索の用途がある段階で、Dictionary(連想配列)を選定していれば問題なくExistsメソッドを活用していけばいいわけですが、Collection(コレクション)でキー検索していくためには、Existsメソッドの代わりにキー検索するための関数を定義していく必要があります。

VBAで定義さえしてしまえば、そのキー検索関数を使い、Existsメソッドと同様な戻り値を得ることができるようになります。

ExistsKey関数

'*********************************************************
'* ExistsKey(Collention内のキー検索関数)
'*********************************************************
'* 第1引数 | Collection | 検索対象となるオブジェクト
'* 第2引数 |   String   | 検索するキー
'*  戻り値 |   Boolan   | True Or False ※False@初期値
'*********************************************************
'*   説明   | 第2引数をキーとしてItemメソッドを実行し、
'*        | 結果をもとにキーの存在を確認する。
'*********************************************************
'*   備考   | オブジェクト未設定の場合 ⇒ 戻り値「False」
'*        | メンバー数「0」の場合 ⇒ 戻り値「False」
'*********************************************************

Function ExistsKey(objCol As Collection, strKey As String) As Boolean
    
    '戻り値の初期値:False
    ExistsKey = False
    
    '変数にCollection未設定の場合は処理終了
    If objCol Is Nothing Then Exit Function
    
    'Collectionのメンバー数が「0」の場合は処理終了
    If objCol.Count = 0 Then Exit Function
    
    On Error Resume Next
    
    'Itemメソッドを実行
    Call objCol.Item(strKey)
        
    'エラー値がない場合:キー検索はヒット(戻り値:True)
    If Err.Number = 0 Then ExistsKey = True

End Function

ロジックの説明および補足

上のExistsKey関数ですが難しいロジックが組まれているわけではありません。

  1. 関数の戻り値の初期値を「False」にする。
  2. Collectionのインスタンスを確認する。
    ※オブジェクトが確認できない場合は処理抜け
  3. Collectionのメンバー数を確認する。
    ※メンバー数が「0」の場合は処理抜け
  4. On Error Resume Nextステートメントでエラー制御。
  5. CollectionのItemメソッドを検索キーをキー指定して実行する。
  6. エラー値を確認し、エラー値「0」であれば戻り値を「True」に設定する。

処理の流れはシンプルなので、引数にCollection(コレクション)と検索キーを設定すればBoolean(True/False)で結果が得られるので、処理抜けの条件分岐は独自仕様ですが、ほぼDictionary(連想配列)のExistsメソッドのように使用することができます。

ExistsKey関数の使い方は以下のとおりです。

Sub ExistsKey_Sample()

    Dim oCol As New Collection
    
    With oCol
        .Add Key:="テレビ", Item:="TV"
        .Add Key:="冷蔵庫", Item:="fridge"
        .Add Key:="炊飯器", Item:="rice cooker"
    End With
    
    If ExistsKey(oCol, "炊飯器") Then
        Debug.Print "炊飯器は存在する。"
    Else
        Debug.Print "炊飯器は存在しない。"
    End If
    
    Set oCol = Nothing
    
End Sub

メソッドではないので自前で定義する形にはなりますが、一度定義してしまえば他のロジックにも流用できるので便利です。
Collection用定義関数ExistsKeyの処理結果
ただし注意点があります。

これはCollection(コレクション)の仕様なので、この関数の定義に問題があるわけではないのですが、CollectionのKey項目は大小文字、全半角を厳密に区別することができません。

仕様上、区別があいまいなため、キー検索に厳密性を求める場合にはDictionary(連想配列)への切り替えを検討する必要があります。

「キーを大小文字、全半角で区別する必要性」については事前にしっかり確認しておきましょう。

Collection(コレクション)の格納データの存在チェック

Collection(コレクション)のAddメソッド時に設定したKeyではなくItemの方を探し、対象データがすでに格納されているのか否かの存在チェックを目的とした関数のご紹介になります。

さすがのDictionary(連想配列)もキー検索のExistsメソッドはサポートされていますが、格納データの存在チェックメソッドについてはサポートされておりません。

格納データを存在チェックするメソッドがない背景にはデータ型を不特定にメンバー追加できる仕様あたりがネックになっている可能性が考えられます。

定義されていないなら格納データを存在チェックする関数をごりごり作っていくだけです。

上で定義した関数と同様に戻り値がBooleanになる関数を目指してみたいと思います。

ExistsItem関数

'*********************************************************
'* ExistsItem(Collentionの格納データの存在チェック関数)
'*********************************************************
'* 第1引数 | Collection | 検索対象となるオブジェクト
'* 第2引数 |  Variant   | 検索するデータ
'* 戻り値  |   Boolan   | True Or False ※False@初期値
'*********************************************************
'*   説明   | 第2引数を検索対象として各メンバーと突合し、
'*        | メンバー内の存在チェック結果を返す。
'*********************************************************
'*   備考   | オブジェクト未設定の場合 ⇒ 戻り値「False」
'*        | メンバー数「0」の場合 ⇒ 戻り値「False」
'*********************************************************

Function ExistsItem(objCol As Collection, varItem As Variant) As Boolean
    
    Dim v As Variant
    
    '戻り値の初期値:False
    ExistsItem = False
    
    '変数にCollection未設定の場合は処理終了
    If objCol Is Nothing Then Exit Function
    
    'Collectionのメンバー数が「0」の場合は処理終了
    If objCol.Count = 0 Then Exit Function
    
    'Collectionの各メンバーと突合
    For Each v In objCol
        
        '突合結果が一致した場合:戻り値「True」にループ抜け
        If v = varItem Then ExistsItem = True: Exit For
        
    Next
    
End Function

ロジックの説明および補足

このExistsItem関数に関しては説明用にシンプルに仕上げました。

  1. 関数の戻り値の初期値を「False」にする。
  2. Collectionのインスタンスを確認する。
    ※オブジェクトが確認できない場合は処理抜け
  3. Collectionのメンバー数を確認する。
    ※メンバー数が「0」の場合は処理抜け
  4. Collectionをループ処理
    • 各メンバーと確認用データを突合
    • 突合結果が同一値と確認された場合は戻り値に「True」に設定
      ※ループ処理抜け

このExistsItem関数については改善の余地があります。

単純な値比較ができるメンバーなら問題なく動作しますが、メンバーに配列や各種オブジェクトが追加された場合にはエラーになってしまいます。

ループ処理内の突合ロジックについては使用するデータ型に応じて変更させる必要があります。

  • 配列については要素すべての完全一致させたい
  • 多次元配列に対応させたい
  • オブジェクトについても参照先が同一が必須としたい
  • データ格納できるオブジェクトは内容すべて完全一致させたい

求める仕様が要件によって異なってくるため、ここでは一番シンプルな仕様を採用しました。

ExistsItem関数の使い方は以下のとおりです。

Sub ExistsItem_Sample()

    Dim oCol As New Collection
    
    With oCol
        .Add Key:="テレビ", Item:="TV"
        .Add Key:="冷蔵庫", Item:="fridge"
        .Add Key:="炊飯器", Item:="rice cooker"
    End With
    
    If ExistsItem(oCol, "fridge") Then
        Debug.Print "fridgeは存在する。"
    Else
        Debug.Print "fridgeは存在しない。"
    End If
    
    Set oCol = Nothing
    
End Sub

ExistsKey関数と似た雰囲気のあるロジックになりますが、引数にはItem値を設定しています。
Collection用定義関数ExistsItemの処理結果

まとめ

今回はCollection(コレクション)に備わっていない機能を補う関数のご紹介となりましたが、いかがでしたでしょうか。

キー検索や値の存在チェックは使用頻度の高い機能ですし、自前での定義となり必要に応じてカスタマイズすることもできるため、オブジェクトを補う考え方としては意義があるテーマだったのではないかなと思っています。

今回はキー検索や値の存在チェック機能を補う関数でしたが、また便利な関数など別途ご紹介できたらと思います。ではでは。

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