Collectionオブジェクトを使い倒す!

Excelマクロで使用するVBAを用いたデータ処理で、欠かせないのが配列ですが、VBAの各種オブジェクトでも多く使用されているコレクション(Collection)オブジェクトの使い方を勉強していきます!

■複雑な操作

今回は、配列に対して複雑な操作が可能な以下のオブジェクトについて勉強してくわけですが・・

広告

配列や複雑な操作については、前回記載した内容を確認してください

※手は抜いていません!有効利用です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のエラー
重複Keyのエラー

※なお、Keyを指定せずにAddする場合は、このエラーは発生しません

Keyに数字を指定した場合は、以下のエラーが発生します

Keyに数字を指定
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 オブジェクトについて学びたいと思います

ではでは

広告

やもす ʕ•͡-•ʔ

のんびり!のほほん!がモットーです!w 蕎麦食いたい ライブ行きたい 暑いの嫌い

シェアする