Collection vs Dictionary
Collectionオブジェクトは安直にデータをぶっこんで、あとでグルグル回すのに便利です。たぶん、みなさんも使ったことがあると思います。
コレクションにはオブジェクトを格納することもできるので次のようにRangeオブジェクトを入れることができます。For Eachでループさせることもできるし、For i=1 To oCol.Countでループさせることもできます。
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 することにしています。検証はしていませんが、たぶんそうしないと実行時エラーになりそうな気がします。