こんにちは、Mottyです。
久々の投稿となりまして、今回はExcelVBAの「構造体」の機能について記述しました。
概要
構造体とは、複数の変数を1つのグループにまとめたものです。
構造体を使うことで新しく何かができる、ということはないのですが、
変数を1つのグループにまとめて取り扱うことによって
似たような処理をまとめて実行しやすくなったり、コード自体が読みやすくなったりします。
VBAでの記述
VBAで記述することができます。
例として、個人情報の名前にアクセスするコードを記述しました。
構造体を使わずに記述する場合、変数に直接アクセスすることになります。
<構造体なし>
Sub Main() ' 構造体なし
Dim Name As String
Dim ID As String
Dim Adress As String
Dim PhoneNumber As String
Name = "Tanaka"
ID = "001"
Adress = "Tokyo"
PhoneNumber = "090-XXXX-XXXX"
Debug.Print ("名前は" & Name & " です")
' →名前はTanakaです
End Sub
<構造体あり>
これに対して構造体を使用する場合は、UserInfoという塊をまず定義してあげます。
そのあと各変数に値を格納します。
デバッグの際は構造体の"名前"プロパティにアクセスする形になります。
Public Type UserInfo '構造体定義
Name As String
ID As String
Adress As String
PhoneNumber As String
End Type
Sub Main2()
Dim User As UserInfo
User.Name = "Tanaka"
User.ID = "001"
User.Adress = "Tokyo"
User.PhoneNumber = "090-XXXX-XXXX"
Debug.Print ("名前は" & User.Name & " です")
' →名前はTanakaです
End Sub
これだけでは正直違いがわかりませんね・・・。
むしろ構造体を使う方がコード行数が多くなり、面倒なようにも思えます。
結論からいうと、扱う変数の数が多くなれば構造体は役に立ちます。
関連性のあるもの同士を1つのデータ型に格納し、処理等を行うことで
コードがスッキリしますし、情報にもアクセスしやすくなります。
データ処理
複数の生徒のテスト得点に関する情報がExcel上にあり、
任意の科目の平均点をデバッグするコードを記述してみます。
<構造体なし>
Dim first_row As Long
Dim last_row As Long
Dim name_list() As String
Dim ID_list() As String
Dim English_score() As Long
Dim Math_score() As Long
Dim Japanese_score() As Long
Dim Science_score() As Long
Dim Society_score() As Long
Sub Main()
Call Initialize ' 初期化
Debug.Print (Average_("Science"))
End Sub
Sub Initialize() ' 初期化
' SCAN行数
first_row = 2 'Because of Header Exists
last_row = Cells(Rows.Count, 1).End(xlUp).Row ' last of "A" Column
' 変数再定義
ReDim name_list(first_row To last_row)
ReDim ID_list(first_row To last_row)
ReDim English_score(first_row To last_row)
ReDim Math_score(first_row To last_row)
ReDim Japanese_score(first_row To last_row)
ReDim Science_score(first_row To last_row)
ReDim Society_score(first_row To last_row)
'SCAN
For Row = first_row To last_row
name_list(Row) = Cells(Row, 1)
ID_list(Row) = Cells(Row, 2)
English_score(Row) = Cells(Row, 3)
Math_score(Row) = Cells(Row, 4)
Japanese_score(Row) = Cells(Row, 5)
Science_score(Row) = Cells(Row, 6)
Society_score(Row) = Cells(Row, 7)
Next
End Sub
Function Average_(ByVal Subject As String) As Double
first_row = 2 'Because of Header Exists
last_row = Cells(Rows.Count, 1).End(xlUp).Row ' last of "A" Column
NUM = last_row - first_row + 1
sum_score = 0
Select Case Subject
Case "English":
For Row = first_row To last_row
sum_score = sum_score + English_score(Row)
Next
Case "Math":
For Row = first_row To last_row
sum_score = sum_score + Math_score(Row)
Next
Case "Japanese":
For Row = first_row To last_row
sum_score = sum_score + Japanese_score(Row)
Next
Case "Society":
For Row = first_row To last_row
sum_score = sum_score + Society_score(Row)
Next
Case "Science"
For Row = first_row To last_row
sum_score = sum_score + Science_score(Row)
Next
Case Else:
Average_ = -1
End Select
Average_ = sum_score / NUM
End Function
<構造体あり>
各行に存在するデータを構造体(Grade)にまとめて記述していきます。
Public Type Grade
Name As String
ID As String
English As Long
Math As Long
Japanese As Long
Science As Long
Society As Long
End Type
Dim first_row As Long
Dim last_row As Long
Dim Grade_Score() As Grade
Sub Main()
Call Initialize
Debug.Print (Average_("English"))
End Sub
Sub Initialize()
'SCAN行数
first_row = 2
last_row = Cells(Rows.Count, 1).End(xlUp).Row
'変数再定義
ReDim Grade_Score(first_row To last_row)
'SCAN
For Row = first_row To last_row
With Grade_Score(Row)
.Name = Cells(Row, 1)
.ID = Cells(Row, 2)
.English = Cells(Row, 3)
.Math = Cells(Row, 4)
.Japanese = Cells(Row, 5)
.Society = Cells(Row, 6)
.Science = Cells(Row, 7)
End With
Next
End Sub
Function Average_(ByVal Subject As String) As Double
first_row = 2 'Because of Header Exists
last_row = Cells(Rows.Count, 1).End(xlUp).Row ' last of "A" Column
NUM = last_row - first_row + 1
sum_score = 0
Select Case Subject
Case "English":
For Row = first_row To last_row
sum_score = sum_score + Grade_Score(Row).English
Next
Case "Math":
For Row = first_row To last_row
sum_score = sum_score + Grade_Score(Row).Math
Next
Case "Japanese":
For Row = first_row To last_row
sum_score = sum_score + Grade_Score(Row).Japanese
Next
Case "Society":
For Row = first_row To last_row
sum_score = sum_score + Grade_Score(Row).Society
Next
Case "Science"
For Row = first_row To last_row
sum_score = sum_score + Grade_Score(Row).Science
Next
Case Else:
Average_ = -1
End Select
Average_ = sum_score / NUM
End Function
構造体を使うことにより変数同士の関連性がわかりやすいですね。
プロパティへのアクセスも階層的に行えるし、ReDimも一括で行えます。
(いつも思うのですが、VBAの特性上ReDimしなければならない仕様は面倒だと感じます・・・。)
結論
小規模のものであれば構造体のありなしでほとんど変わりませんが、
作成するプログラムが大規模であればあるほど、構造体は有効になってきます。
(PCの処理に例えるなら、扱うファイルが数十個〜数百個と多くなると
きっちりフォルダを作って階層的にした方がスッキリしますが、
ファイルが高々数個の場合はそのような処理はほとんど影響しない、といった
感じでしょうか。。)
他にもメリット等あれば、教えていただけるとありがたいです。