③ 指定した全要素に対して処理を繰り返すループ処理
【For Each...Next ループ】
For Each...Next ループは、指定した全要素に対して処理を繰り返す場合に使用します。主に、配列やコレクションといったオブジェクトの要素に対して処理を行う場合に便利です。
構文:
For Each...Next ループの構文は以下のようになります。
Dim "要素" As "データ型"
For Each "要素" In "コレクション"
'・・・処理・・・'
Next "要素"
For Eachの中で用いる要素の変数を必ず最初に宣言しておく必要があります。(意外とこれが忘れがちなので注意してください)
では以下で、具体的な使用例を見ていきたいと思います。
使用例:
セルやワークシートなどのオブジェクトに対して処理を実行
例. セル列を処理する場合(A1-A5に1-5の数値が入力されているとする)
Sub ForEachLoop()
Dim cell As Range
For Each cell In Range("A1:A5")
Debug.Print cell.Value
Next cell
End Sub
出力結果
1
2
3
4
5
では、上記の処理を逆順に処理するにはどうしたら良いでしょうか。
単純にRangeの中身を逆にして実行してみたいと思います。
例. セル列を逆順に処理する場合(Rangeの中身を逆にする)
Sub ForEachLoop()
Dim cell As Range
For Each cell In Range("A5:A1")
Debug.Print cell.Value
Next cell
End Sub
出力結果
1
2
3
4
5
あれ上手くいかないですね...。
実は、For Each...Nextループでは、ループを回す順番を指定できません。
また、コレクションの並び順を逆にしてくれるReverse的なメソッドやステートメントもVBAには存在しません。
そのため、逆順に処理したい場合は、上で紹介したFor...Next ループの Step を -1にすることで実現させるしかなさそうです。
今度はセル行の処理を見ていきます。
例. セル行を処理する場合(A1-E1に1-5の数値が入力されているとする)
Sub ForEachLoop()
Dim cell As Range
For Each cell In Range("A1:E1")
Debug.Print cell.Value
Next cell
End Sub
出力結果
1
2
3
4
5
セル列同様に順番に処理されているのが分かるかと思います。
では続いて、複数の行列に対しての処理を 3×3(A1~C3)の範囲で見ていきたいと思います。
例. セル行列を処理する場合(A1-C3に1-9の数値が入力されているとする)
Sub ForEachLoop()
Dim cell As Range
For Each cell In Range("A1:C3")
Debug.Print cell.Value
Next cell
End Sub
出力結果
1
2
3
4
5
6
7
8
9
複数の行列でも順番に処理されるのが確認されました。
最後にワークシートオブジェクトに対する処理も見ていきたいと思います。
例. ワークシート内のすべてのシート名を表示
Sub ForEachLoop()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Sheets
Debug.Print ws.Name
Next ws
End Sub
出力結果
実行
Sheet1
ThisWorkbook.Sheetsとすることで、現在開いているexcelファイル内の全てのシートオブジェクトが取得できます。
また、ws.Nameとすることでシート名が取得できます。これですべてのシートに対して処理ができるようになりました。
配列やコレクション、ディクショナリに対して処理を実行
For Eachの中で用いる要素のデータ型はVariantでなければなりません。
それ以外のデータ型を入れるとエラーとなります。
例. 配列に対して処理する場合
Sub ForEachLoop()
Dim Data() As Variant
Dim Item As Variant
ReDim Data(2)
Data(0) = "A"
Data(1) = "B"
Data(2) = "C"
For Each Item In Data
Debug.Print Item
Next Item
End Sub
出力結果
A
B
C
ここで一つ注意なのが、上記のような可変長配列で、Variant型を扱う場合のみ、For Eachを用いることができます。
For Each は、対象の配列やコレクションが 列挙可能(Enumerable) なオブジェクトである必要があります。
しかし、通常の固定長配列や明示的なデータ型で定義された配列は列挙可能ではありません。そのため、以下のような固定長配列やデータ型を指定する場合は、For Eachを用いることができないです。
例. 配列に対して処理する場合(固定長配列でデータ型を指定)
Sub ForEachLoop()
Dim Data(1 To 3) As String
Dim Item As Variant
Data(0) = "A"
Data(1) = "B"
Data(2) = "C"
For Each Item In Data
Debug.Print Item
Next Item
End Sub
以下は、Arrayに対しての処理の例になります。
Array関数を使う場合は、初期化した際に戻り値がVariant型で返ってくるため、変数の宣言時は必ずVariant型にします(Dim DataArray As Variant)。他の型を指定するとエラーが発生してしまいます(例えば Dim DataArray As String などはエラー)。
例. Arrayに対して処理する場合
Sub ForEachLoop()
Dim DataArray As Variant
Dim Item As Variant
DataArray = Array("A", "B", "C")
For Each Item In DataArray
Debug.Print Item
Next Item
End Sub
出力結果
A
B
C
次にコレクションでの処理について見ていきたいと思います。
例. コレクションに対して処理する場合
Sub ForEachLoop()
Dim DataCollection As Collection
Dim Item As Variant
Set DataCollection = New Collection
DataCollection.Add "A"
DataCollection.Add "B"
DataCollection.Add "C"
For Each Item In DataCollection
Debug.Print Item
Next Item
End Sub
出力結果
A
B
C
最後に、ディクショナリでの処理について見ていきます。
例. ディクショナリに対して処理する場合
Sub ForEachLoop()
Dim DataDictionary As Object
Dim key As Variant
Set DataDictionary = CreateObject("Scripting.Dictionary")
DataDictionary.Add "A", "Aのデータです"
DataDictionary.Add "B", "Bのデータです"
DataDictionary.Add "C", "Cのデータです"
For Each key In DataDictionary.Keys
Debug.Print key & ": " & DataDictionary(key)
Next key
End Sub
出力結果
A: Aのデータです
B: Bのデータです
C: Cのデータです
For Each...Nextループの使用可否をまとめた図が以下となります。
配列の種類 | For Each 使用可否 | 説明 |
---|---|---|
Range, Worksheetsなど | 可能 | Excelオブジェクトでも使える |
Variant型配列 | 可能 | Array()などで生成される |
Dim arr(1 To 3) As Integer | 不可 | 通常の固定長配列はNG |
Collection, Dictionary | 可能 | 問題なく使える |
自身で作成したクラスオブジェクトに対して処理を実行
例. クラスオブジェクトに対して処理する場合(使用するデータはエクセルシートから取得)
'クラスモジュール(Student)
Option Explicit
Private FamilyName_ As String
Private FirstName_ As String
Private Const AttendanceDone As String = "出席しました:"
Public Property Let FamilyName(FamilyNameValue As String)
FamilyName_ = FamilyNameValue
End Property
Public Property Let FirstName(FirstNameValue As String)
FirstName_ = FirstNameValue
End Property
Public Property Get FullName() As String
FullName = FamilyName_ & FirstName_
End Property
Sub Attendance()
MsgBox AttendanceDone & FullName
End Sub
Sub ForEachLoop()
Dim DataCollection As Collection
Dim Item As Variant
Set DataCollection = New Collection
DataCollection.Add "A"
DataCollection.Add "B"
DataCollection.Add "C"
For Each Item In DataCollection
Debug.Print Item
Next Item
End Sub
出力結果
A
B
C
ループを強制的に抜けるには...
ループを強制的に抜けるやり方を見ていきます。
For Each...Nextループを抜けるには、For...Next ループと同様に Exit For を使います。
例. 特定の条件の時にループを抜ける場合(For Each...Next ループ)
Sub ForEachLoop()
Dim DataArray As Variant
Dim Item As Variant
DataArray = Array("A", "B", "C")
For Each Item In DataArray
If Item = "B" Then
Exit For ' 条件に一致したらループ終了
End If
Debug.Print Item
Next Item
End Sub
出力結果
A
特定の条件の時だけ処理をスキップして、それ以降も引き続き処理を行う場合は、以下のように GoTo を利用します。
例. 特定の条件の時だけ処理をスキップする場合(For Each...Next ループ)
Sub ForEachLoop()
Dim DataArray As Variant
Dim Item As Variant
DataArray = Array("A", "B", "C")
For Each Item In DataArray
If Item = "B" Then GoTo CONTINUE:
Debug.Print Item
CONTINUE:
Next Item
End Sub
出力結果
A
C
まとめ
VBAのループ処理には様々な方法があり、用途によって適切なものを選ぶことが重要です。
-
For...Next:回数が決まっている場合
-
Do While:条件が満たされる間処理を続ける
-
Do Until:条件が満たされるまで処理を続ける
-
For Each...Next:コレクションや配列の要素を処理する場合
For Each...Next ループを使うメリットとして、・要素の数が不明な場合でも使いやすい ・書き方が簡潔で可読性が高い、といった点が挙げられます。
逆にデメリットとしては、・配列の中でもVariant型配列などに限定される ・ループを回す順番を指定できない、といった点が挙げられます。そのため、格納されているデータに型の不整合があった場合などに、予期せぬエラーが発生することもあります。また、ループを回す順番を指定できないため、逆順に処理したいといった場合などはカウンタ変数を用いる「For...Next ループ」を使うことになります。
他の記事へ
Excel VBA でよく使う繰り返し処理【①For...Next ループ】
Excel VBA でよく使う繰り返し処理【②Do While ループ/Do Until ループ(Do ループ While/Do ループ Until)】