Excelシート上のデータ構造のまま、クラスとCollectionでVBA上のデータとして扱う

Excelのシート上にあるデータ構造のまま、VBA上で扱いたい場合、クラスとCollectionでそれを実現する方法です。
# データ構造例
データ構造は次のような従業員データとします。
![Excelシート.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430322/154c0747-c100-2448-d12b-6e5bf76ee4cb.png)

# クラスを作成
クラスの作成方法は右クリックで、挿入→クラス モジュール の選択です。
![Class.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430322/24bbf8ad-37f8-d1cd-7a70-c616ab44c6e1.png)

今回は EmployeeClassという名前にします。
そこにシート同じ項目の変数(プロパティ)を Public で作成します。

```vb:EmployeeClass.cls
Public Id As String             ' ID
Public Name As String           ' 名前
Public Department  As String    ' 部署
```
![class2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430322/8916d824-b9e9-654b-07c5-fdf4ef74ae83.png)

※定義した変数しか使用しないため、Option Explicit を付けるかどうかは任意とします。

# モジュールの追加
新しく 標準モジュールを挿入します。
![標準モジュール作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430322/08719647-23ec-cbf8-2add-61d06d412ae8.png)

今回はEmployeeModule という名前にします。

```vb:EmployeeModule.bas

Option Explicit

Private Const EMPLOYEE_SHEET_NAME As String = "従業員データ"    ' シート名
Private Const EMPLOYEE_ID_COLUMN_NUM As Long = 1                ' IDの行番号
Private Const EMPLOYEE_NAME_COLUMN_NUM As Long = 2              ' 名前の行番号
Private Const EMPLOYEE_DEPARTMENT_COLUMN_NUM As Long = 3        ' 部署の行番号

' 全従業員データを取得する
Public Function GetAllEmployeeData() As Collection
    Dim employeeData As EmployeeClass
    Dim resultList As Collection    ' 結果を格納する Collection型
    Set resultList = New Collection ' その初期化
    Dim rowNum As Long
    rowNum = 2  ' データは二行目から始まるため
    
    ' シート名で以下のセルを指定
    With ThisWorkbook.Worksheets(EMPLOYEE_SHEET_NAME)
        ' ID行は必ず存在する値であるから、そこが空になるまで繰り返す
        Do While IsEmpty(.Cells(rowNum, EMPLOYEE_ID_COLUMN_NUM)) = False
            Set employeeData = GetEmployeeData(rowNum)  ' 指定行の従業員データを取得する
            Call resultList.Add(employeeData)   ' 結果の変数に格納する
            rowNum = rowNum + 1 ' 次の行へ
            DoEvents    ' 繰り返しが多すぎると制御できなくなるのでOS側に制御を渡してあげる
        Loop
    End With
    Set GetAllEmployeeData = resultList
End Function


' 指定行の従業員データを取得する
Private Function GetEmployeeData(rowNum As Long) As EmployeeClass
    Dim employeeData As EmployeeClass
    With ThisWorkbook.Worksheets(EMPLOYEE_SHEET_NAME)
        Set employeeData = New EmployeeClass
        ' ここで各セルから値を変数にセット
        employeeData.Id = .Cells(rowNum, EMPLOYEE_ID_COLUMN_NUM)
        employeeData.Name = .Cells(rowNum, EMPLOYEE_NAME_COLUMN_NUM)
        employeeData.Department = .Cells(rowNum, EMPLOYEE_DEPARTMENT_COLUMN_NUM)
    End With
    
    Set GetEmployeeData = employeeData
End Function

```
GetAllEmployeeData() と GetEmployeeData() に分けているのは、データの項目数が多くなった時に、この方が修正がしやすくなるからです。
また GetAllEmployeeData() を Public にすることで、外のモジュールからでも呼び出せるようにしてあります。

# 実行例
試しに実行してみます。適当なモジュールで次のようなSubプロシージャを作成します。

```vb:Module1.bas
Sub Start()
    Dim employeeList As Collection
    Dim employeeData As EmployeeClass
    Set employeeList = GetAllEmployeeData
    
    Debug.Print "従業員数は" & employeeList.Count & "人です。"
    
    For Each employeeData In employeeList
        Debug.Print employeeData.Name & "(" & employeeData.Id & ")さんの部署は" & employeeData.Department & "です。"
    Next
End Sub

```
すると、従業員の全情報が、employeeList に格納され、その中身はEmployeeClassとして扱えることが分かります。
### 実行画面
![実行例.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430322/059d7cee-5d46-cf51-fccc-d99b6ddbfb06.png)

以上がExcelシートのデータ構造のままVBAで扱う方法になります。

# 参考資料
[VBAでクラスをつくる](https://qiita.com/Kamo123/items/a4c7749fa30d8f68df28)
[VBAでCollectionとdictionaryを使う](https://qiita.com/pregum/items/071f72969d72d90cf826)
[Excel VBAコーディング ガイドライン案](https://qiita.com/mima_ita/items/8b0eec3b5a81f168822d)
[VBA コーディングガイドライン](http://www.thom.jp/vbainfo/codingguideline.html)