Collectionオブジェクトを使い倒す!
Excelマクロで使用するVBAを用いたデータ処理で、欠かせないのが配列ですが、VBAの各種オブジェクトでも多く使用されているコレクション(Collection)オブジェクトの使い方を勉強していきます!
■複雑な操作
今回は、配列に対して複雑な操作が可能な以下のオブジェクトについて勉強してくわけですが・・
配列や複雑な操作については、前回記載した内容を確認してください
[blogcard url=”https://zesys.net/excel/vba/use-arrays-with-vba/”]VBAで配列を使う[/blogcard]
※手は抜いていません!有効利用ですw
■Collectionオブジェクト
今回は、記事のタイトルでも書いた通り、今回はCollectionオブジェクトを勉強します!(Dictionaryオブジェクトも後日勉強しますw)
参考サイト:「Collection オブジェクト | Microsoft Docs」
Collectionオブジェクトは参照設定が不要ですので、ダイレクトにCollectionオブジェクトを記載できます
説明するよりもサンプルを見てもらった方が早いかも知れません
・いきなりサンプル
CollectionオブジェクトにString型の値をAddして、For~EachステートメントでVariant型の変数に取り出すサンプルです
Sub Sample1()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add "Mon"
obj.Add "Tue"
obj.Add "Wed"
obj.Add "Thu"
obj.Add "Fri"
obj.Add "Sat"
obj.Add "Sun"
' For~EachステートメントでCollectionオブジェクトをVariant型の変数に取り出す
For Each week In obj
Debug.Print week
Next
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Collectionオブジェクトのメリットと言えば、Addメソッドで配列を動的に確保していけるので、ReDimステートメントを使用して、予め配列を確保する必要がありません
・Keyを用いたサンプル
あまり知られていない(?)かも知れませんが、CollectionオブジェクトにはKeyを使用して、Addする方法があります
実際に、MicrosoftのAddメソッドのサンプルは、Keyを使ったサンプルが公開されているのです!
Worksheetsオブジェクトでシート名をキーにして、Worksheetオブジェクトを取得できるのと同じように扱うことができます
Sub Sample2()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
Dim idx As Integer
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add Key:="Mon", Item:="Monday"
obj.Add Key:="Tue", Item:="Tuesday"
obj.Add Key:="Wed", Item:="Wednesday"
obj.Add Key:="Thu", Item:="Thursday"
obj.Add Key:="Fri", Item:="Friday"
obj.Add Key:="Sat", Item:="Saturday"
obj.Add Key:="Sun", Item:="Sunday"
' インデックスを使用してItemを取り出す
Debug.Print "①インデックスを使用してItemを取り出す"
For idx = 1 To obj.Count
Debug.Print obj(idx)
Next
Debug.Print
' Keyを使用してItemを取り出す
Debug.Print "②Keyを使用してItemを取り出す"
Debug.Print obj("Mon")
Debug.Print obj("Tue")
Debug.Print obj("Wed")
Debug.Print obj("Thu")
Debug.Print obj("Fri")
Debug.Print obj("Sat")
Debug.Print obj("Sun")
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
①インデックスを使用してItemを取り出す
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
②Keyを使用してItemを取り出す
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
– 注意点
インデックスで取り出す場合、ReDimステートメントが「0」からなのに対して、Collectionオブジェクトは「1」から開始することに注意が必要です
Keyを指定した場合には、更に以下の注意点があります
- 重複するKeyはAddできない
- Keyに指定できるのは文字列
ちなみに、重複するKeyをAddしようとすると、以下のエラーが発生します
※なお、Keyを指定せずにAddする場合は、このエラーは発生しません
Keyに数字を指定した場合は、以下のエラーが発生します
– デメリット
ただ、Keyを用いた場合、Itemへのアクセスは一意な名称で可能になるので、便利なのですが、以下のようなデメリットがあります
- Keyの重複をチェックできるメソッドがない
- Keyを取り出す方法がない
- Addメソッドのエラーでしか判断できない
CollectionオブジェクトはKeyを用いたAddが可能ですが、Keyが既にコレクションに含まれていることを調べるメソッドが用意されていません
更に、Keyを取り出す方法もないため、Keyが既に含まれていることを調べることもできない
つまり、Addしてエラーになったら重複Keyがあるなど、あまり宜しくない対応が必要になります
・クラスを用いたサンプル(推奨)
そこで推奨したい実装としては、クラスをCollectionオブジェクトにAddする方法です
Weekクラスを定義して、WeeksのCollectionオブジェクトにクラスをAddするサンプルです
For~Eachステートメントで取り出す場合もWeekクラスで取り出すことができます
(WorksheetクラスのコレクションであるWorksheetsオブジェクトと同じイメージです)
Sub Sample3()
Dim Weeks As Collection ' Collectionオブジェクト
Dim Wk As week ' クラス定義
' Collectionオブジェクトの生成
Set Weeks = New Collection
' clsWeekクラスを生成
Set Wk = New week
Wk.m_no = 1
Wk.m_week = "Mon"
Wk.m_name = "Monday"
' 作成したclsWeekクラスをCollectionオブジェクトに追加する
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 2
Wk.m_week = "Tue"
Wk.m_name = "Tuesday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 3
Wk.m_week = "Wed"
Wk.m_name = "Wednesday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 4
Wk.m_week = "Thu"
Wk.m_name = "Thursday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 5
Wk.m_week = "Fri"
Wk.m_name = "Friday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 6
Wk.m_week = "Sat"
Wk.m_name = "Saturday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = New week
Wk.m_no = 7
Wk.m_week = "Sun"
Wk.m_name = "Sunday"
Weeks.Add Key:=Wk.m_no, Item:=Wk
Set Wk = Nothing
' For~EachステートメントでclsWeekクラスの変数に取り出す
For Each Wk In Weeks
Debug.Print Wk.m_no, Wk.m_week, Wk.m_name
Next
' Collectionオブジェクトを破棄する
Set Weeks = Nothing
End Sub
■出力結果
1 Mon Monday
2 Tue Tuesday
3 Wed Wednesday
4 Thu Thursday
5 Fri Friday
6 Sat Saturday
7 Sun Sunday
クラスをCollectionオブジェクトのItemとする場合、重複したクラスデータを取り込むことが可能になります
一意性を保つために、クラスに一意な「No」をメンバに持たせて、これをCollectionオブジェクトのKeyに設定することで、一意性を維持して、Itemへのアクセスを簡単にすることが可能になります
これは一覧表などに「No」列を持たせて、一意性を保つのと同じ考えです
– Weekクラス
メンバしかいないシンプルなクラスを作ってみますw
Option Explicit ' メンバ Public m_no As String ' No. Public m_week As String ' 名称(短) Public m_name As String ' 名称(長)
メンバだけなら構造体でもいいんですけどねー
汎用的なことを考慮して、クラスにしています
そしてシレっとString型のみのメンバ構成にしています
ExcelからTextプロパティで値を取得するので、String型で扱うのが癖になったというだけのことですが、気になる方は、「No」をInteger型やLong型にしても良いかと思います
クラスにクラスそのものを返すゲッターを作成して、CollectionにAddするというのもよく使用します
■メソッド
・Addメソッド
サンプルの中で使用しているので、使い方は概ねわかっていただけているかと思いますが、紹介していないオプションがあるので、それだけ説明します
– Before
AddメソッドでBeforeを指定することで、Collectionオブジェクトに含まれている既存の要素の前にItemを挿入することができます
インデックス番号を指定するサンプルを記載します
Sub Sample4()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add "Mon" ' 1
obj.Add "Tue" ' 2
obj.Add "Wed" ' 3
obj.Add "Fri" ' 4
obj.Add "Sat" ' 5
obj.Add "Sun" ' 6
' Beforeで「4」"Fri"の前に"Thu"を追加する
obj.Add Item:="Thu", Before:=4
' For~EachステートメントでVariant型の変数に取り出す
For Each week In obj
Debug.Print week
Next
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
Mon
Tue
Wed
Thu
Fri
Sat
Sun
– After
Before同様に、AddメソッドでAfterを指定することで、Collectionオブジェクトに含まれている既存の要素の後にItemを挿入することができます
Keyの文字列を指定するサンプルを記載します
Sub Sample5()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add Key:="Mon", Item:="Monday"
obj.Add Key:="Tue", Item:="Tuesday"
obj.Add Key:="Wed", Item:="Wednesday"
obj.Add Key:="Fri", Item:="Friday"
obj.Add Key:="Sat", Item:="Saturday"
obj.Add Key:="Sun", Item:="Sunday"
' Afterで"Wed"の後に"Thu"を追加する
obj.Add Key:="Thu", Item:="Thursday", After:="Wed"
' For~EachステートメントでVariant型の変数に取り出す
For Each week In obj
Debug.Print week
Next
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
・Itemメソッド
あまり使いませんけど、Collectionオブジェクトの規定メンバなので、紹介しておきます
Sub Sample6()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
Dim idx As Integer
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add "Mon"
obj.Add "Tue"
obj.Add "Wed"
obj.Add "Thu"
obj.Add "Fri"
obj.Add "Sat"
obj.Add "Sun"
' Itemを使用する
For idx = 1 To obj.Count
Debug.Print obj.Item(idx), obj(idx)
Next
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
Mon Mon
Tue Tue
Wed Wed
Thu Thu
Fri Fri
Sat Sat
Sun Sun
Sample6の20行目と出力結果を見て頂ければわかるように、以下の書き方で得られる情報は同じです
- obj.Item(idx)
- obj(idx)
なので、あまりItemメソッドは使いませんが、Itemメソッドでも引数にKeyの文字列を指定することは可能です
・Removeメソッド
Addメソッドの逆で、Collectionオブジェクトから要素を削除する場合に使用します
Sub Sample7()
Dim obj As Collection ' Collectionオブジェクト
Dim week As Variant ' For~Eachステートメントで使用できる型
Dim idx As Integer
' Collectionオブジェクトの生成
Set obj = New Collection
' String型の文字列をCollectionオブジェクトに追加する
obj.Add "Mon"
obj.Add "Tue"
obj.Add "Wed"
obj.Add "Thu"
obj.Add "Fri"
obj.Add "Sat"
obj.Add "Sun"
' 4番目の要素を削除する
obj.Remove(4)
' Itemを使用する
For idx = 1 To obj.Count
Debug.Print obj.Item(idx)
Next
' Collectionオブジェクトを破棄する
Set obj = Nothing
End Sub
■出力結果
Mon
Tue
Wed
Fri
Sat
Sun
これはReDimステートメントで作成する配列にはない便利なメソッドです
残念なところは、他のメソッドと違って、Keyでは削除できないというところですね
■プロパティ
・Countプロパティ
Collectionオブジェクトのメンバで、Collectionオブジェクト内の要素数を返してくれるプロパティです
オブジェクトブラウザで見ると、メソッドに割り当たってるんですけどね!?
使い方は、Sample2でシレっと使っていますので、そちらをご覧くださいw
指定するインデックスの上限として、Forステートメントを使ってループする場合に使用します
■最後に
大量のデータを扱う上では、CollectionオブジェクトはAddやRemoveができるので非常に便利な配列として重宝されます
この便利さでも十分なのですが、より便利な配列用のオブジェクトとして、Dictionary オブジェクトが用意されています
次回は、このDictionary オブジェクトについて学びたいと思います
ではでは




