オブジェクトがVBAのコレクションのメンバーであるかどうかを確認するにはどうすればよいですか?
具体的には、テーブル定義がTableDefs
コレクションのメンバーであるかどうかを調べる必要があります。
最善の策は、コレクションのメンバーを反復処理し、探しているものと一致するものがあるかどうかを確認することです。私を信じて、私はこれを何度もしなければなりませんでした。
2番目の解決策(さらに悪いこと)は、「コレクションにないアイテム」エラーをキャッチし、アイテムが存在しないことを示すフラグを設定することです。
十分じゃないですか?
Public Function Contains(col As Collection, key As Variant) As Boolean
Dim obj As Variant
On Error GoTo err
Contains = True
obj = col(key)
Exit Function
err:
Contains = False
End Function
正確ではありませんが、見つけることができる最良の(そして最も速い)ソリューションはOnErrorを使用することでした。これは、中規模から大規模のコレクションの反復よりも大幅に高速になります。
Public Function InCollection(col As Collection, key As String) As Boolean
Dim var As Variant
Dim errNumber As Long
InCollection = False
Set var = Nothing
Err.Clear
On Error Resume Next
var = col.Item(key)
errNumber = CLng(Err.Number)
On Error GoTo 0
'5 is not in, 0 and 438 represent incollection
If errNumber = 5 Then ' it is 5 if not in collection
InCollection = False
Else
InCollection = True
End If
End Function
これは古い質問です。すべての回答とコメントを注意深く確認し、パフォーマンスのソリューションをテストしました。
コレクションにオブジェクトとプリミティブが含まれている場合に失敗しない、環境にとって最速のオプションを思い付きました。
Public Function ExistsInCollection(col As Collection, key As Variant) As Boolean
On Error GoTo err
ExistsInCollection = True
IsObject(col.item(key))
Exit Function
err:
ExistsInCollection = False
End Function
さらに、このソリューションはハードコードされたエラー値に依存しません。したがって、パラメータcol As Collection
は、他のコレクション型変数で置き換えることができ、関数は引き続き機能する必要があります。たとえば、現在のプロジェクトでは、col As ListColumns
。
このソリューションは、コレクションを反復処理するためのMicrosoftソリューションと混合した上記の提案から作成しました。
Public Function InCollection(col As Collection, Optional vItem, Optional vKey) As Boolean
On Error Resume Next
Dim vColItem As Variant
InCollection = False
If Not IsMissing(vKey) Then
col.item vKey
'5 if not in collection, it is 91 if no collection exists
If Err.Number <> 5 And Err.Number <> 91 Then
InCollection = True
End If
ElseIf Not IsMissing(vItem) Then
For Each vColItem In col
If vColItem = vItem Then
InCollection = True
GoTo Exit_Proc
End If
Next vColItem
End If
Exit_Proc:
Exit Function
Err_Handle:
Resume Exit_Proc
End Function
このために提案されたコードを短くしたり、予期しないエラーを一般化したりすることができます。どうぞ:
Public Function InCollection(col As Collection, key As String) As Boolean
On Error GoTo incol
col.Item key
incol:
InCollection = (Err.Number = 0)
End Function
私はいくつかの編集があり、コレクションに最適です:
Public Function Contains(col As collection, key As Variant) As Boolean
Dim obj As Object
On Error GoTo err
Contains = True
Set obj = col.Item(key)
Exit Function
err:
Contains = False
End Function
特定のケース(TableDefs)では、コレクションを反復処理し、名前を確認するのが良い方法です。コレクションのキー(名前)はコレクション内のクラスのプロパティであるため、これは問題ありません。
ただし、VBAコレクションの一般的な場合、キーは必ずしもコレクション内のオブジェクトの一部ではありません(たとえば、コレクション内のオブジェクトとは関係のないキーを使用して、コレクションを辞書として使用できます)。この場合、アイテムにアクセスしてエラーをキャッチする以外に選択肢はありません。
このバージョンは、プリミティブ型およびクラスに対して機能します(短いテストメソッドが含まれます)
' TODO: change this to the name of your module
Private Const sMODULE As String = "MVbaUtils"
Public Function ExistsInCollection(oCollection As Collection, sKey As String) As Boolean
Const scSOURCE As String = "ExistsInCollection"
Dim lErrNumber As Long
Dim sErrDescription As String
lErrNumber = 0
sErrDescription = "unknown error occurred"
Err.Clear
On Error Resume Next
' note: just access the item - no need to assign it to a dummy value
' and this would not be so easy, because we would need different
' code depending on the type of object
' e.g.
' Dim vItem as Variant
' If VarType(oCollection.Item(sKey)) = vbObject Then
' Set vItem = oCollection.Item(sKey)
' Else
' vItem = oCollection.Item(sKey)
' End If
oCollection.Item sKey
lErrNumber = CLng(Err.Number)
sErrDescription = Err.Description
On Error GoTo 0
If lErrNumber = 5 Then ' 5 = not in collection
ExistsInCollection = False
ElseIf (lErrNumber = 0) Then
ExistsInCollection = True
Else
' Re-raise error
Err.Raise lErrNumber, mscMODULE & ":" & scSOURCE, sErrDescription
End If
End Function
Private Sub Test_ExistsInCollection()
Dim asTest As New Collection
Debug.Assert Not ExistsInCollection(asTest, "")
Debug.Assert Not ExistsInCollection(asTest, "xx")
asTest.Add "item1", "key1"
asTest.Add "item2", "key2"
asTest.Add New Collection, "key3"
asTest.Add Nothing, "key4"
Debug.Assert ExistsInCollection(asTest, "key1")
Debug.Assert ExistsInCollection(asTest, "key2")
Debug.Assert ExistsInCollection(asTest, "key3")
Debug.Assert ExistsInCollection(asTest, "key4")
Debug.Assert Not ExistsInCollection(asTest, "abcx")
Debug.Print "ExistsInCollection is okay"
End Sub
コレクション内のアイテムがオブジェクトではなく配列である場合、追加の調整が必要です。それ以外は私にとってはうまくいった。
Public Function CheckExists(vntIndexKey As Variant) As Boolean
On Error Resume Next
Dim cObj As Object
' just get the object
Set cObj = mCol(vntIndexKey)
' here's the key! Trap the Error Code
' when the error code is 5 then the Object is Not Exists
CheckExists = (Err <> 5)
' just to clear the error
If Err <> 0 Then Call Err.Clear
Set cObj = Nothing
End Function
ソース: http://coderstalk.blogspot.com/2007/09/visual-basic-programming-how-to-check.html
収集にキーが使用されていない場合:
Public Function Contains(col As Collection, thisItem As Variant) As Boolean
Dim item As Variant
Contains = False
For Each item In col
If item = thisItem Then
Contains = True
Exit Function
End If
Next
End Function
私のコードではありませんが、かなりうまく書かれていると思います。 Object要素自体だけでなくキーによるチェックも可能にし、On ErrorメソッドとすべてのCollection要素の繰り返しの両方を処理します。
https://danwagner.co/how-to-check-if-a-collection-contains-an-object/
リンクされたページで利用できるため、完全な説明はコピーしません。将来的にページが最終的に利用できなくなった場合に備えて、ソリューション自体がコピーされました。
コードについて疑問があるのは、最初のIfブロックでのGoToの過剰使用ですが、誰でも簡単に修正できるので、元のコードはそのままにしておきます。
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'INPUT : Kollection, the collection we would like to examine
' : (Optional) Key, the Key we want to find in the collection
' : (Optional) Item, the Item we want to find in the collection
'OUTPUT : True if Key or Item is found, False if not
'SPECIAL CASE: If both Key and Item are missing, return False
Option Explicit
Public Function CollectionContains(Kollection As Collection, Optional Key As Variant, Optional Item As Variant) As Boolean
Dim strKey As String
Dim var As Variant
'First, investigate assuming a Key was provided
If Not IsMissing(Key) Then
strKey = CStr(Key)
'Handling errors is the strategy here
On Error Resume Next
CollectionContains = True
var = Kollection(strKey) '<~ this is where our (potential) error will occur
If Err.Number = 91 Then GoTo CheckForObject
If Err.Number = 5 Then GoTo NotFound
On Error GoTo 0
Exit Function
CheckForObject:
If IsObject(Kollection(strKey)) Then
CollectionContains = True
On Error GoTo 0
Exit Function
End If
NotFound:
CollectionContains = False
On Error GoTo 0
Exit Function
'If the Item was provided but the Key was not, then...
ElseIf Not IsMissing(Item) Then
CollectionContains = False '<~ assume that we will not find the item
'We have to loop through the collection and check each item against the passed-in Item
For Each var In Kollection
If var = Item Then
CollectionContains = True
Exit Function
End If
Next var
'Otherwise, no Key OR Item was provided, so we default to False
Else
CollectionContains = False
End If
End Function