VBAには辞書構造がありますか?


回答:


341

はい。

MSスクリプトランタイム(「Microsoftスクリプトランタイム」)への参照を設定します。@regjoのコメントに従って、[ツール]→[参照]に移動し、[Microsoft Scripting Runtime]のボックスをオンにします。

参照ウィンドウ

以下のコードを使用して辞書インスタンスを作成します。

Set dict = CreateObject("Scripting.Dictionary")

または

Dim dict As New Scripting.Dictionary 

使用例:

If Not dict.Exists(key) Then 
    dict.Add key, value
End If 

Nothing使い終わったら、必ず辞書を設定してください。

Set dict = Nothing 

17
このデータ構造タイプは、VBAではなくスクリプトランタイムによって提供されます。基本的に、VBAは、COMインターフェイスを介してアクセス可能な実質的にすべてのデータ構造タイプを使用できます。
David-W-Fenton、

163
完全を期すために、これが機能するように「Microsoft Scripting Runtime」を参照し(「ツール」->「参照」に移動)、そのボックスをチェックする必要があります。
regjo 2009

7
ええと、VBAコレクションはキー付きです。しかし、おそらく別の定義がありkeyedます。
David-W-Fenton

8
Excel 2010を使用していますが、「Microsoft Scripting Runtime」ツールへの参照がありません-参照。CreateObjectを実行するだけでは機能しません。だから、@ masterjo上記のコメントは間違っていると思います。私が何かを欠落していない限り..したがって、みんなのツール->参照が必要です。
ihightower 2012年

4
Dim dict As New Scripting.Dictionary参考までに、リファレンスなしでを使用することはできません。参照がなければ、CreateObjectこのオブジェクトをインスタンス化する遅延バインディングメソッドを使用する必要があります。
David Zemens 2013

179

VBAにはコレクションオブジェクトがあります。

    Dim c As Collection
    Set c = New Collection
    c.Add "Data1", "Key1"
    c.Add "Data2", "Key2"
    c.Add "Data3", "Key3"
    'Insert data via key into cell A1
    Range("A1").Value = c.Item("Key2")

Collectionそれは速いですので、ハッシュを使用してオブジェクトを実行するキーベースの検索。


あなたは使うことができます Contains()関数を、特定のコレクションにキーが含まれているかどうかを確認ます。

Public Function Contains(col As Collection, key As Variant) As Boolean
    On Error Resume Next
    col(key) ' Just try it. If it fails, Err.Number will be nonzero.
    Contains = (Err.Number = 0)
    Err.Clear
End Function

2015年6月24日を編集Contains()@TWiStErRobのおかげで短くなりました。

2015年9月25日編集Err.Clear()@scipilotへの感謝を追加。


5
Addメソッドにはオプションの「キー」引数があるため、組み込みのCollectionオブジェクトを指摘するためによくできました。辞書として使用できます。
Simon Tewsi 2013

8
コレクションオブジェクトの悪い点は、キーが既にコレクション内にあるかどうかを確認できないことです。エラーをスローするだけです。それは大きなことです。コレクションについては好きではありません。(私は知っている、回避策があるが、それらのほとんどは「醜い」)
MiVoth

5
VBAディクショナリ内の文字列キー(例:c.Item( "Key2"))のルックアップはハッシュ化されていますが、整数インデックス(例:c.Item(20))によるルックアップはそうではないことに注意してください。スタイル検索のため、避けてください。文字列キーの検索のみ、または各反復でコレクションを使用するのが最適です。
ベンマッキンタイア

4
私は短いものを見つけましたContainsOn Error Resume Next_ col(key)_Contains = (Err.Number = 0)
TWiStErRob

5
おそらく、関数に名前を付ける必要がありContainsKeyます。呼び出しだけを読んでいる人は、特定の値が含まれていることを確認するために、呼び出しを混乱させる可能性があります。
jpmc26

44

VBAにはディクショナリの内部実装はありませんが、VBAからはMSスクリプトランタイムライブラリのディクショナリオブジェクトを引き続き使用できます。

Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"

If d.Exists("c") Then
    MsgBox d("c")
End If

29

出現頻度を含めるのに役立つ追加の辞書の例。

ループの外:

Dim dict As New Scripting.dictionary
Dim MyVar as String

ループ内:

'dictionary
If dict.Exists(MyVar) Then
    dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
    dict.Item(MyVar) = 1 'set as 1st occurence
End If

頻度を確認するには:

Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
    Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i

1
追加のチュートリアルリンクは次のとおり
John M

これは非常に良い答えで、私はそれを使いました。しかし、ループの中でdict.Items(i)またはdict.Keys(i)を参照することができないことがわかりました。ループに入る前にそれら(アイテムリストとキーリスト)を別々の変数に格納し、それらの変数を使用して必要な値に到達する必要がありました。Like-allItems = companyList.Items allKeys = companyList.Keys allItems(i)そうでない場合、Keys(i)にアクセスしようとすると「プロパティletプロシージャが定義されておらず、プロパティgetプロシージャはオブジェクトを返しませんでした」というエラーが発生します。ループ内のアイテム(i)。
raddevus 2017年

10

オフの構築cjrhの答え、私たちは(私はラベルを使用して好きではない)は、ラベルを必要としない機能が含まれて構築することができます。

Public Function Contains(Col As Collection, Key As String) As Boolean
    Contains = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            Contains = False
            err.Clear
        End If
    On Error GoTo 0
End Function

私のプロジェクトでは、Collection動作をのようにするための一連のヘルパー関数を作成しましたDictionary。それでも再帰的なコレクションが可能です。キーは必須であり、私の実装ではより理にかなっているため、キーが常に最初に来ることに気付くでしょう。私も使っただけStringキー使用しました。必要に応じて元に戻すことができます。

セットする

古い値を上書きするため、この名前をsetに変更しました。

Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
    If (cHas(Col, Key)) Then Col.Remove Key
    Col.Add Array(Key, Item), Key
End Sub

取得する

errを使用setしてオブジェクトを渡し、それを使用せずに変数を渡すため、これらはオブジェクト用です。対象物かどうかはチェックできると思いますが、時間を要しました。

Private Function cGet(ByRef Col As Collection, Key As String) As Variant
    If Not cHas(Col, Key) Then Exit Function
    On Error Resume Next
        err.Clear
        Set cGet = Col(Key)(1)
        If err.Number = 13 Then
            err.Clear
            cGet = Col(Key)(1)
        End If
    On Error GoTo 0
    If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function

持っている

この投稿の理由...

Public Function cHas(Col As Collection, Key As String) As Boolean
    cHas = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            cHas = False
            err.Clear
        End If
    On Error GoTo 0
End Function

削除する

存在しない場合はスローしません。削除されたことを確認するだけです。

Private Sub cRemove(ByRef Col As Collection, Key As String)
    If cHas(Col, Key) Then Col.Remove Key
End Sub

キー

キーの配列を取得します。

Private Function cKeys(ByRef Col As Collection) As String()
    Dim Initialized As Boolean
    Dim Keys() As String

    For Each Item In Col
        If Not Initialized Then
            ReDim Preserve Keys(0)
            Keys(UBound(Keys)) = Item(0)
            Initialized = True
        Else
            ReDim Preserve Keys(UBound(Keys) + 1)
            Keys(UBound(Keys)) = Item(0)
        End If
    Next Item

    cKeys = Keys
End Function

6

スクリプティングランタイムディクショナリには、高度な段階で設計を台無しにするバグがあるようです。

ディクショナリ値が配列の場合、ディクショナリへの参照を通じて配列に含まれる要素の値を更新することはできません。


6

はい。VB6、VBA(エクセル)、およびVB.NET


2
さらに質問を読むことができます:私はVBAについて尋ねました:VB、VB.Net、その他の言語ではなく、アプリケーション用のVisual Basic。

1
fessGUID:もう一度、答えをもっと読むべきです!この回答はVBA(特に最初のリンク)にも使用できます。
Konrad Rudolph、

5
私は認めます。質問を早く読みすぎました。しかし、私は彼が何を知る必要があるかを彼に話しました。
Matthew Flaschen、2009年

5
@ Oorang、VBAがVB.NETのサブセットになる証拠はまったくありません。Officeのバックコンパットルールです。これまでに作成されたすべてのExcelマクロを変換しようとしていると想像してみてください。
Richard Gadsden

2
VBAは、実際にはVB6のスーパーセットです。VB6と同じコアDLLを使用しますが、Officeの特定のアプリケーションにあらゆる種類の機能を追加します。
David-W-Fenton、

4

何らかの理由で、Excelに追加機能をインストールできない場合、またはインストールしたくない場合は、少なくとも単純な問題に対しては配列も使用できます。WhatIsCapitalとして国の名前を入力すると、関数はその首都を返します。

Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String

WhatIsCapital = "Sweden"

Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")

For i = 0 To 10
    If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i

Debug.Print Answer

End Sub

1
この回答の概念は適切ですが、サンプルコードは記述どおりに実行されません。各変数は独自の必要Dimのキーワードを、CountryそしてCapital必要性が原因の使用にバリアントとして宣言されるようにArray()i宣言する(と場合でなければならないはずOption Explicitのセットがある)、およびループカウンタは、バインドされたエラーのうちをスローするように起こっている-より安全な値に使用UBound(Country)Toます。また、Array()関数は便利なショートカットですが、VBAで配列を宣言する標準的な方法ではないことにも注意してください。
jcb 2018

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.