For Eachとは? 配列・セル範囲・コレクションを1つずつ処理する方法
今回の目標
「For Eachって? Forとは違うの?」そんな疑問を持ったあなたへ。
この記事では、For Eachの使い方から、配列・セル範囲・コレクションでの活用例、
さらには“やりがちな失敗例”まで、まとめて丁寧に解説します!
基本構文だけでなく、実務で使える6つの具体例つきです。
目標リスト
- For Each について理解を深める
- 配列・セル範囲・コレクションをFor Eachで1つずつ処理できるようになる
- 配列やセル範囲のループ処理をFor Eachで簡潔に書けるようになる
- For と For Eachの違いを理解し、使い分けられるようになる
説明
For Eachとは
For Each は、コレクション(Collection型や、配列やセル範囲、コレクションなどの複数の要素を持つオブジェクトのこと)から、1つずつ順番に要素を取り出して、指定した変数に代入しながら処理を繰り返す構文です。
原則、要素の先頭から順番に処理されますが、環境により取り出す要素の順番が変わる場合があります。
※コレクションの型の例:配列、Workbooks、Worksheets、Range、Shapes など
構文は次の通りです。
For Each A In B(コレクション)
'処理
Next A
Bから説明します。
B には、コレクションを入れます。コレクションとは先で述べた通り、配列やセル範囲、コレクションなどの複数の要素を持つオブジェクトのことです。
A は、変数である必要があり、Bを1つに分解した要素を格納します。Bの要素のデータがリテラル値の場合、Aの変数の型はVariant型でなければなりません。
For Eachの使用例6選
1.配列の各要素を表示させる
1次元配列の全ての要素に対し、イミディエイトウィンドウに格納されているデータを表示させます。
Sub ForEachSample1()
Dim arr() As Variant
Dim buf As Variant
arr = Array("250円", "リンゴ")
'配列の要素分、ループする
For Each buf In arr
' 250円 → リンゴ の順で表示される
Debug.Print buf
Next buf
End Sub
2.A1~A3のセルを操作する
A1~A3のセルを太字にして、末尾に「様」を結合します。
Sub ForEachSample2()
Dim rng As Range
'2.A1~A3のセルを操作する
For Each rng In Range("A1:A3")
rng.Font.Bold = True
rng = rng & "様"
Next
End Sub
3.全てのシート名を表示する
マクロを実行しているブックのシートを全てイミディエイトウィンドウに表示します。
Sub ForEachSample3()
Dim ws As Worksheet
'シートの枚数だけ、ループする
For Each ws In ThisWorkbook.Worksheets
Debug.Print ws.Name
Next
End Sub
4.シート内の図形の色やフォントを変更する
アクティブなシート内に存在するオートシェイプの図形を操作します。
Sub ForEachSample4()
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
'オートシェイプが対象(画像等は非対象)
If shp.Type = msoAutoShape Then
'図形の背景色を黄緑色にする
shp.Fill.ForeColor.RGB = RGB(180, 255, 180)
'図形内の文字色を黒にする
shp.TextFrame.Characters.Font.Color = RGB(0, 0, 0)
'図形内の文字のフォントをメイリオにする
shp.TextFrame.Characters.Font.Name = "メイリオ"
End If
Next
End Sub
5.名前の定義をされた範囲をループ処理する
「名前の定義」とは、Excelでセルやセル範囲に「総務部」や「財務部」など任意の名前をつける機能です。
例えば、セルB4に「消費税」という名前を定義しておけば、数式で =100*消費税、VBAでは Range("消費税") として扱うことができます。
For Eachでは、名前の定義された範囲のセルを1つずつ処理することも可能です。
Sub ForEachSample5()
Dim rng As Range
'名前の定義「総務部」の範囲のセルを操作する
For Each rng In Range("総務部")
Debug.Print rng
Next rng
End Sub
6.Collection と併用する
Collection に要素を格納後、For Each で各要素を取り出すことが出来ます。
Sub ForEachSample6()
Dim ccn As New Collection
Dim buf As Variant
'Collection に要素を追加
ccn.Add "りんご"
ccn.Add "ごりら"
ccn.Add "らっぱ"
ccn.Add "ぱせり"
'For Each で1つずつ表示
For Each buf In ccn
Debug.Print buf
Next
End Sub
For Eachを使うメリットは?
For 構文より、可読性が高い!
見やすさ(可読性)が大きく向上します。
For 構文を使用してセルの操作を行う際、Cells若しくはRangeでどのセルを指定しているかを1ステップずつ、確認しなければなりません。
しかし、For Each 構文でコレクションから1つずつ要素を変数に格納する場合はこれがありません。また、プログラミングする際もタイプミスがなく、プログラマーにも優しい機能です。
For 構文と For Each構文で比較します。
C3~C10の値が30未満、65未満で、それぞれ背景色を変更するプログラムです。
まずは、For 構文を使用したサンプルです。
Sub ForEachMerit1_1()
Dim i As Long
For i = 3 To 10
If Cells(i, 3) < 30 Then
Cells(i, 3).Interior.Color = RGB(255, 0, 0)
ElseIf Cells(i, 3) < 65 Then
Cells(i, 3).Interior.Color = RGB(255, 255, 0)
End If
Next i
End Sub
For Each 構文を使用したサンプルです。
Sub ForEachMerit1_2()
Dim cel As Range
For Each cel In Range("C3:C10")
If cel < 30 Then
'背景色を赤色にする
cel.Interior.Color = RGB(255, 0, 0)
ElseIf cel < 65 Then
'背景色を黄色にする
cel.Interior.Color = RGB(255, 255, 0)
End If
Next cel
End Sub
可読性が上がると、作成者以外がプログラムを読みやすくなるだけでなく、不具合が生じた際に素早く原因を特定することが出来ます。
For Eachを使うべきでない場面
For Eachは順番が保証されない場合がある!
For Each で配列の処理を行う場合、原則として要素数が少ない要素から最大要素に向かって処理を行います(例:arr(0)→arr(1)→arr(2)・・・)。しかし、これは仕様として定められておらず、将来変更される可能性があります。
そのため、処理する順番が重要な場合は、For Each構文でなく、For構文を使用してください。
二次元配列や複数行x複数列の処理順番
表示される順番は大半の環境で下記の説明のようになりますが、仕様で明言されていないため説明の順序を前提にしない方が安全です。
二次元配列の処理順番
原則となりますが、二次元配列の場合、原則として1次元配列から処理し、終わり次第二次元配列をずらします。
この処理の順番は、ユーザーの環境や仕様変更により、変更される可能性があります。
'2次元1.配列の各要素を表示させる
Sub ForEachActNum1()
Dim arr(2, 1) As Variant
Dim buf As Variant
arr(0, 0) = "0, 0"
arr(1, 0) = "1, 0"
arr(2, 0) = "2, 0"
arr(0, 1) = "0, 1"
arr(1, 1) = "1, 1"
arr(2, 1) = "2, 1"
'原則として次の順に表示される
'0, 0 → 1, 0 → 2, 0 → 0, 1 → 1, 1 → 2, 1
For Each buf In arr
Debug.Print buf
Next
End Sub
複数行x複数列の範囲の処理順番
原則となりますが、セルの範囲を指定した時は、原則として一番左の上のセルから一番左の下のセルを処理し、1列ずつ右にずらしながら処理を行います。 この処理の順番は、ユーザーの環境や仕様変更により、変更される可能性があります。
'A1:B2の範囲を指定した時のどのセルから処理するかを確認する
Sub ForEachActNum2()
Dim rng As Range
'大半の環境では、$A$1 → $B$1 → $A$2 → $B$2 と表示される
For Each rng In Range("A1:B2")
Debug.Print rng.Address
Next
End Sub
途中でループを抜ける Exit For
For 構文と同様に「Exit For」を使用すると、ループから脱出できます。
コレクションの中で特定の値を来たら、それ以降の処理が不要な場合に使用します。
下記サンプルでは「150円」と「バナナ」と表示され、「200円」と「リンゴ」は表示されません。
Sub ForEachExit()
Dim arr() As Variant
Dim buf As Variant
arr = Array("150円", "バナナ", -1, "200円", "リンゴ")
For Each buf In arr
' -1 が来たら、ループから抜ける
If (buf = -1) Then
Exit For
Else
Debug.Print buf
End If
Next buf
End Sub
For Eachのよくあるミス3選
1.非対応の型を使ってしまった
コレクションがオブジェクトでない場合、受け取りは Variant型でなければなりません。
Sub ForEachCommonMistakes1()
Dim arr() As Variant
Dim str As String '←Variantが正しい
arr = Array("1", "リンゴ", "200円")
'コンパイルエラーが発生する
For Each str In arr '←arr はString型の配列
Debug.Print str
Next str
End Sub
2.ループ中に要素を削除してしまった
ループ中に要素を削除してしまったと意図しない動作になることがあります。
セルA1~A10を削除するとき、次のプログラムを作成したとします。
Sub ForEachCommonMistakes2()
Dim buf As Variant
For Each buf In Range("A1:A10")
buf.Delete
Next
End Sub
実行すると、次のようになります(環境等により、稀に結果が変わります)。

上記、図のように削除できていないセルがあります。このように、要素を削除する場面では、For Each を使用しないようにしてください。
余談ですが、セルや行を削除する場合は、For 構文を使用して、削除する最も下の行から削除する最も上の行まで繰り返し削除しましょう。
3.コレクションが空の状態で処理してしまった
コレクションの設定が出来ていない場合です。
実行時エラーで処理が止まってしまうパターンや、処理が止まらず論理エラーとなるパターンがあります。
実行時エラーで処理が止まってしまうパターンです。
Sub ForEachCommonMistakes3_1()
Dim arr() As Variant
Dim val As Variant
'実行時エラー '92' Forループが初期化されていません
For Each val In arr '← ここでエラー
Debug.Print val
Next val
End Sub
arr は動的配列として宣言されていますが、要素数が決まっていない(初期化されていない)ため、For Each で繰り返し処理を始めることができず、「実行時エラー '92' Forループが初期化されていません」となります。
処理が止まらず論理エラーとなるパターンがあります。
Sub ForEachCommonMistakes3_2()
Dim ccn As New Collection
Dim buf As Variant
'実行時エラーにならないが、ccnが空のため、For Each に入らない
For Each buf In ccn
'中に入らないので、1回も表示されない
Debug.Print buf
Next
End Sub
ccn は空の Collection なので、For Each はループに入らず、何も処理されません。エラーにもならず、原因の発覚に時間がかかります。