Collection内に要素が存在するかチェックする方法を解説します。
これを行う標準機能は用意されていませんので、
愚直にFor Each文で一致する要素があるかを判定します。
- はじめに~Dictionaryオブジェクトについて
- CollectionのItemにある値が存在するかチェック
- Collectionにあるオブジェクトが存在するかチェック
- CollectionにRangeオブジェクトが存在するかチェック
- Collectionの要素存在チェックを行う汎用関数
- CollectionにあるKeyが存在するかチェック
はじめに~Dictionaryオブジェクトについて
Collectionと似た仕組みで、Dictionary(連想配列)というものがあります。
このDictionaryには「Existsメソッド」がありますので、
Collectionとは異なり要素の存在チェックが一発で終わります。
また、Dictionaryには「一次元配列(Array)に変換する」という、
これもCollectionにはない便利な機能を有しています。
Dictionaryは「KeyとItemをペアで持つ仕組み」なのですが、
Itemを無視してKeyだけで運用すればCollectionとしても使えます。
もし今回のコードを実装したいマクロをまだ作り始めたくらいなら、
今からCollectionの存在チェックを頑張るよりも、
CollectionをDictionaryに置き換える方が実装が簡単でメンテナンスも楽です。
まずはそのことを念頭に置いて、以下の記事をお読みいただくか、
本記事をこのまま読み進めるかをお選びいただければと思います。
CollectionのItemにある値が存在するかチェック
CollectionのItemにある値が存在するかチェックには、
以下のコードを実行します。
Sub コレクション内の要素存在チェックサンプル() Dim コレクション As New Collection コレクション.Add "みかん" コレクション.Add "りんご" コレクション.Add "いちご" コレクション.Add "さくらんぼ" ' コレクション内に「みかん」があるかを確認 Dim is存在判定 As Boolean: is存在判定 = False Dim 要素 For Each 要素 In コレクション If 要素 = "みかん" Then is存在判定 = True Exit For End If Next If is存在判定 Then MsgBox "みかんは存在します。" Else MsgBox "みかんは存在しません。" End If End Sub
For Each文でコレクション内をすべてループし、
一致する値があるかを判定しているストレートなコードです。
このコードに特に注意点はありませんが、
CollectionはFor文でループすると遅くなる点はご注意ください。
Collectionの全件ループには上記のようにFor Each文を使いましょう。
詳しくはこちらをどうぞ。
汎用関数化
上記ForEach文を存在判定のたびに書くのは大変なので、
この判定を何度も行う場合は汎用関数化しておきましょう。
Sub コレクション内の要素存在チェックの汎用関数使用サンプル() Dim コレクション As New Collection コレクション.Add "みかん" コレクション.Add "りんご" コレクション.Add "いちご" コレクション.Add "さくらんぼ" ' コレクション内に「みかん」があるかを確認 If IsCollection内に要素が存在(コレクション, "みかん") Then MsgBox "みかんは存在します。" Else MsgBox "みかんは存在しません。" End If End Sub ' Collectionの要素存在チェック関数 Function IsCollection内に要素が存在(コレクション As Collection, 判定値 As Variant) As Boolean Dim 要素 For Each 要素 In コレクション If 要素 = 判定値 Then IsCollection内に要素が存在 = True Exit Function End If Next End Function
使用例を見てわかる通り非常にわかりやすいコードになります。
Collectionをよく使用する方はこの汎用関数を持っておきましょう。
ただし、繰り返しになりますが、これはDictionaryなら一発で終わる話です。
' ディクショナリに「みかん」があるかを確認 If ディクショナリ.Exists("みかん") Then
この汎用関数を用意しておくのは便利ですが、
そもそもDictionaryが使えるならその方がよい点は留意してください。
Collectionにあるオブジェクトが存在するかチェック
Collectionにはオブジェクトも格納できるため、
その場合はFor Each文を以下の通り書き換えます。
Sub コレクション内のオブジェクト要素存在チェックサンプル() Dim コレクション As New Collection コレクション.Add Worksheets("みかん") コレクション.Add Worksheets("りんご") コレクション.Add Worksheets("いちご") コレクション.Add Worksheets("さくらんぼ") ' コレクション内に「みかん」名のシートがあるかを確認 Dim is存在判定 As Boolean: is存在判定 = False Dim 要素 For Each 要素 In コレクション If 要素 Is Worksheets("みかん") Then ' ←「=」を「Is」に変える is存在判定 = True Exit For End If Next If is存在判定 Then MsgBox "みかんシートは存在します。" Else MsgBox "みかんシートは存在しません。" End If End Sub
オブジェクトは判定が「Is」ですのでご注意ください。
CollectionにRangeオブジェクトが存在するかチェック
オブジェクトは「Is」で判定ができるとしましたが、
RangeオブジェクトだけはIs演算子では判定できません。
この場合は「ブック・シートも含めたセルアドレス」が等しいかで判定してください。
Sub コレクション内のRangeオブジェクト要素存在チェックサンプル() Dim コレクション As New Collection コレクション.Add Range("A1") コレクション.Add Range("B1") コレクション.Add Range("C1") コレクション.Add Range("D1") ' コレクション内にA1セルがあるかを確認 Dim is存在判定 As Boolean: is存在判定 = False Dim 要素 For Each 要素 In コレクション ' ブック名とシート名も含めたセルアドレス「Address(External:=True)」で判定 If 要素.Address(External:=True) = Range("A1").Address(External:=True) Then is存在判定 = True Exit For End If Next If is存在判定 Then MsgBox "A1セルは存在します。" Else MsgBox "A1セルは存在しません。" End If End Sub
なお、RangeオブジェクトをCollectionに格納できるということは、
裏を返せば.Valueを省略するとセルそのものが格納されてしまいます。
セル値をCollectionに入れたい場合は、必ず.Valueを明記して下さい。
Collectionの要素存在チェックを行う汎用関数
先ほど「ある値が存在するか」を判定する汎用関数を作りましたが、
これに「オブジェクト・セル」の判定も入れた万能関数がこちらになります。
' Collectionの要素存在チェック関数 Function IsCollection内に要素が存在(コレクション As Collection, 判定要素 As Variant) As Boolean Dim 要素 ' ◇ セル If TypeName(判定要素) = "Range" Then For Each 要素 In コレクション If TypeName(要素) = "Range" Then If 要素.Address(External:=True) _ = 判定要素.Address(External:=True) Then IsCollection内に要素が存在 = True Exit Function End If End If Next ' ◇ セル以外のオブジェクト ElseIf IsObject(判定要素) Then For Each 要素 In コレクション If 要素 Is 判定要素 Then IsCollection内に要素が存在 = True Exit Function End If Next ' ◇ 値 Else For Each 要素 In コレクション If 要素 = 判定要素 Then IsCollection内に要素が存在 = True Exit Function End If Next End If End Function
' これらすべて同じ関数で動く If IsCollection内に要素が存在(コレクション, "みかん") Then If IsCollection内に要素が存在(コレクション, Worksheets("みかん")) Then If IsCollection内に要素が存在(コレクション, Range("A1")) Then
単に引数の種類に応じて回すForEach文を分けているだけですね。
IsObject関数やTypeName関数を用いれば汎用関数を万能化できます。
実装が簡単な割に効果が大きいので活用してみて下さい。
CollectionにあるKeyが存在するかチェック
CollectionにはItemだけでなくKeyもセットで渡すことができます。
' コレクションに商品名と商品コードをセットで搭載 Dim コレクション As New Collection コレクション.Add "みかん", "A001" コレクション.Add "りんご", "A002" コレクション.Add "いちご", "A003" コレクション.Add "さくらんぼ", "A004"
このとき、「みかん」が存在するかではなく、
「A001」をKeyとするItemが存在するかを判定する方法を解説します。
ただし、KeyとItemをセットで登録するのであれば、
それこそこれはDictionaryの役割です。
Keyを省略できるというのが唯一のCollectionのメリットであるため、
Key-Itemの登録にCollectionを用いる意味はありません。
以降のコードは
「Dictionaryが使えないMacユーザーがCollectionをDictionaryの代わりに使う」
ためのコードだと思ってください。
Windowsユーザーはこちらの記事をどうぞ。
Sub コレクション内のKey存在チェックサンプル() ' コレクションに商品名と商品コードをセットで搭載 Dim コレクション As New Collection コレクション.Add "みかん", "A001" コレクション.Add "りんご", "A002" コレクション.Add "いちご", "A003" コレクション.Add "さくらんぼ", "A004" ' コレクション内にKey「A001」があるかを確認 On Error Resume Next Dim is存在判定 As Boolean Dim x: x = コレクション("A001") is存在判定 = (Err.Number = 0) On Error GoTo 0 If is存在判定 Then MsgBox "A001は存在します。" Else MsgBox "A001は存在しません。" End If End Sub
Collection内に特定のKeyが存在するかチェックする場合は、
コレクション.Item(判定Key)がエラーになるかを調べます。
汎用関数にしたい場合はこちらをご利用ください。
' CollectionのKey存在チェック Function IsCollectionに指定Keyが登録済(コレクション As Collection, 判定Key As Variant) As Boolean On Error Resume Next Dim is存在判定 As Boolean Dim x: x = コレクション(判定Key) IsCollectionに指定Keyが登録済 = (Err.Number = 0) On Error GoTo 0 End Function