Collection vs Dictionary

Collectionオブジェクトは安直にデータをぶっこんで、あとでグルグル回すのに便利です。たぶん、みなさんも使ったことがあると思います。

コレクションにはオブジェクトを格納することもできるので次のようにRangeオブジェクトを入れることができます。For Eachでループさせることもできるし、For i=1 To oCol.Countでループさせることもできます。

 

f:id:gungnir46:20191107233102p:plain

Public Sub test1()

    Dim xCell As Excel.Range
    Dim oCol As Collection

    Set xCell = ActiveSheet.Range("A2")
    Set oCol = New Collection

    Do Until xCell.Text = ""
        oCol.Add xCell, xCell.Text
        Set xCell = xCell.Offset(1, 0)
    Loop

    For Each xCell In oCol
        Debug.Print xCell.Offset(0, 1)
    Next

End Sub


test1の実行結果

稲垣 啓太
堀江 翔太
具 智元 
トンプソン ルーク
ジェームス・ムーア 
リーチ マイケル
ピーター・ラブスカフニ 
姫野 和樹 
流 大 
田村 優
福岡 堅樹
中村 亮土 
ラファエレ ティモシー 
松島 幸太朗
山中 亮平 
坂手 淳史 
中島 イシレリ 


コレクションオブジェクトのデメリット
便利なコレクションオブジェクトですが、不便な点もあります。 

すでに登録済みのKeyを持つデータを更新したい場合などが特に煩雑で、次のようなケースでは「このキーは既にこのコレクションの要素に割り当てられています」というエラーになってしまいます。そのため、Removeでキーを削除しておく必要があります。

Dim sKey As String
sKey = "6501"
oCol.Add 1, sKey
oCol.Add oCol(sKey) + 1, sKey <== エラー

 

 

コレクションオブジェクトの上位版

Dictionaryオブジェクトを使うとこのような煩雑さは解消されます。このライブラリを使うにはツール▶▶参照設定で"Microsoft Scripting Runtime"を登録してください。

Dictionary.Add( Key, Item )
Dictionary.Exist(Key)
Dictionary.Item(Key)
Dictionary.Items(Index)
Dictionary.Key(Key)
Dictionary.Keys(Index)
Dictionary.Remove
Dictionary.RemoveAll
Dictionary.Count

 

パグやチワワをキーとして扱い、何匹いるのか個体をカウントするサンプル。

Public Sub test2()

    Dim i As Long
    Dim oDic As Dictionary

 

    Set oDic = New Dictionary
    oDic.Item("ネコ") = oDic.Item("ネコ") + 1
    oDic.Item("ネコ") = oDic.Item("ネコ") + 1
    oDic.Item("ネコ") = oDic.Item("ネコ") + 1
    oDic.Item("ネコ") = oDic.Item("ネコ") + 1
    oDic.Item("パグ") = oDic.Item("パグ") + 1
    oDic.Item("パグ") = oDic.Item("パグ") + 1
    oDic.Item("チワワ") = 777
    For i = 0 To oDic.Count - 1
        Debug.Print oDic.Keys(i) & ":" & oDic.Items(i)
    Next

    If oDic.Item("パグ") = 2 Then Debug.Print "パグ2匹"

End Sub

 

test2の実行結果

ネコ:4
パグ:2
チワワ:777
パグ2匹

 

Public Sub test3()

    Dim xCell As Excel.Range
    Dim oDic As Dictionary
    Dim i As Long

 

    Set oDic = New Dictionary
    Set xCell = ActiveSheet.Range("A2")
    Do Until xCell.Text = ""
        Set oDic.Item(xCell.Text) = xCell    ’記述方法が若干違うことに注意
        Set xCell = xCell.Offset(1, 0)
    Loop

    For i = 0 To oDic.Count - 1  'コレクションは1起点
        Set xCell = oDic.Items(i)
        Debug.Print xCell.Offset(0, 1)
    Next

End Sub

 

test3の実行結果 

test1と同じ結果になります。

 

コレクションオブジェクトの上位版のようなDictionaryオブジェクトですが、唯一できないことがあって、それはFor Eachでループさせることができないということです。コレクションにはないRemoveAllやExistなどがあるので、常にDictionaryオブジェクトだけでコードを書いても別に問題はないと思います。

 

余談ですが、DictionaryオブジェクトにRangeオブジェクトを格納するときにインスタンスを作る必要はないのかと疑問に思ってしまった人は、昔のActiveXインターフェースの基礎が分かっている人です。私は、自分が定義したクラスのインスタンスを格納したい場合は気持ち悪いので、毎回 = new することにしています。検証はしていませんが、たぶんそうしないと実行時エラーになりそうな気がします。