和風スパゲティのレシピ

日本語でコーディングするExcelVBA

DictionaryのItemからKeyを取得する

DictionaryはKeyからItemを取得するオブジェクトですが、
逆にItemからKeyを取得する方法を解説します。

といってもそんなプロパティはありませんので、
自作関数を作ってゴリ押しするよりほかありません。

ItemからKeyを取得する関数

はじめに、Dictionaryは「Key→Itemを紐づける」仕組みですので、
KeyはユニークですがItemはユニークではありません。

よってItemからKeyを取得するといっても、
結果のKeyがひとつとは限らないという点に注意してください。


こちらの対応策として、

  • 最初に見つかったものを返す
  • 複数あったらカンマ区切りで返す
  • 常に配列を返す(一意に定まっても要素1の配列を返す)

の3パターンを作りましたのでセットでお持ち帰りください。


なお、「KeyもItemもすべて値」という想定で作成しております。
どちらかにオブジェクトをいれたDictionaryへの対応は適宜、自作してください。

最初に見つかったKeyを返す版

' 最初に見つかったものを返すVer
Function GetDictionaryKey←Item_第1key(対象Dic As Dictionary, 対象Item As Variant) As Variant
    
    Dim key
    For Each key In 対象Dic.Keys
    
        If 対象Dic(key) = 対象Item Then
            GetDictionaryKey←Item_第1key = key
            Exit Function
        End If
    Next
    
End Function

特に注意点などありません。
ストレートに「全Keyをループ→Itemが一致したらEixt」を実行したコードです。

複数見つかったらカンマ区切りで返す版

' カンマ区切りで返すVer
Function GetDictionaryKey←Item_カンマ区切り(対象Dic As Dictionary, 対象Item As Variant _
    , Optional 区切り文字 As String = ",") As String
    
    Dim 結果値 As String
    
    Dim key
    For Each key In 対象Dic.Keys
    
        If 対象Dic(key) = 対象Item Then
            結果値 = 結果値 & 区切り文字 & key
        End If
    Next
    
    ' 最初の区切り文字を消して返す
    If 結果値 <> "" Then GetDictionaryKey←Item_カンマ区切り = Mid(結果値, Len(区切り文字) + 1)
    
End Function

カンマと言いつつ元データがカンマを含む可能性があるので、
区切り文字を指定できる(省略時はカンマ)仕様としました。


注意点としてKeyの型が変わる可能性があり、
元のKeyがLong型だったとしても、String型で返ってしまいます。

区切り文字で返す以上、関数内ではどうしようもありませんので、
呼出元のコードで対応してください。

常に配列を返す版

' 常に配列を返すVer
Function GetDictionaryKey←Item_配列(対象Dic As Dictionary, 対象Item As Variant)
    
    Dim 区切り文字 As String: 区切り文字 = "◆◇"
    
    GetDictionaryKey←Item_配列 = Split( _
        GetDictionaryKey←Item_カンマ区切り(対象Dic, 対象Item, 区切り文字), 区切り文字)
    
End Function

Dictionary内の配列をいじるのは面倒なので、
先ほどのカンマ版をSplitして対応しています。

正確にはStringの配列が返りますので、
先ほどと同様Keyの型が変わる可能性があることにご注意ください。


小ネタですが、最初に見つかったkeyを返す版も、

GetDictionaryKey←Item_配列(対象Dic,対象Item)(0)

で作れたりします。

これだと発見時のExit Functionによる高速化が出来なくなるので、
ちゃんと別に作りましたけどね。

KeyとItemを反転させたDictionaryを作る

上記の方法はすべてのItemごとにすべてのKeyをループするので、
もし全ItemでこれをやるとなるとO(n²)と激遅になってしまいます。


これを解決するには、

  1. 一旦KeyとItemを反転させたDictionaryを作る
  2. そのDictionaryの全key(元Dictionaryの全Item)をループする

という方法で対応します。

KeyとItemを反転させたDictionaryの生成関数がこちらです。

' Key⇔Item反転Dictionaryの生成
Function KeyとItemを反転させたDictionaryを生成(元Dic As Dictionary) As Dictionary

    ' 一旦区切り文字を使ったDictionaryを生成
    Dim 区切り文字 As String: 区切り文字 = "◆◇"
    Dim 反転Dic_区切りstr As New Dictionary

    Dim 元key
    For Each 元key In 元Dic.Keys
        Dim 元Item: 元Item = 元Dic(元key)
    
        If 反転Dic_区切りstr.Exists(元Item) = False Then
            反転Dic_区切りstr.Add 元Item, 元key
        Else
            反転Dic_区切りstr(元Item) = 反転Dic_区切りstr(元Item) & 区切り文字 & 元key
        End If
    
    Next
    
    ' 全ItemをSplitで配列化
    Dim 反転Dic_Arr As New Dictionary
    Dim key
    For Each key In 反転Dic_区切りstr.Keys
    
        反転Dic_Arr.Add key, Split(反転Dic_区切りstr(key), 区切り文字)
    
    Next
    
    Set KeyとItemを反転させたDictionaryを生成 = 反転Dic_Arr
End Function

Item(元DictionaryのKey)は汎用性を考え配列として持ちました。


例えば全Item-Keyのリストをイミディエイトウィンドウに出力するには、
以下のコードを実行すればよいことになります。

Dim 反転Dic As New Dictionary
Set 反転Dic = KeyとItemを反転させたDictionaryを生成(元dic)

Dim key
For Each key In 反転Dic.Keys

    Debug.Print key, Join(反転Dic(key))
Next


すべてのItemからKeyを逆取得するようなときは、この関数をご活用ください。