目次
はじめに
データ処理では「特定列のデータだけ欲しい」「ヘッダー行は除外したい」「非表示行は処理したくない」といった要件が組み合わさることがよくあります。今回は、3つの関数を組み合わせて、柔軟にデータ範囲を絞り込む方法を紹介します。
3つの関数を組み合わせる利点
データ範囲を絞り込む処理を1つの大きな関数にまとめることもできますが、それぞれの処理を独立した関数に分けることで、以下のような利点があります。
まず、各関数が単一の責任(1つの処理)だけを持つため、コードが読みやすくなります。「この関数は列を絞り込む」「この関数はヘッダーを除外する」と、名前を見ただけで何をする関数か分かります。
次に、必要な処理だけを組み合わせて使えるため、柔軟性が高まります。例えば「ヘッダーは除外したいけど非表示セルは含めたい」という場合は、可視セル取得の関数を呼ばなければ良いだけです。
さらに、各関数を他の処理でも再利用できるため、コードの重複が減ります。特定列の取得関数は、このデータ処理以外でも使える汎用的な関数になります。
このような設計を「責任の分離」と言い、保守性(メンテナンスのしやすさ)が高いコードを書く上で重要な考え方です。
各関数の役割
今回使用する3つの関数は、それぞれ以下の役割を持ちます。
【GetBodyRange関数】
範囲からヘッダー行(1行目)を除外してデータ部分だけを取得する関数です。Offsetで1行下に移動し、Resizeで行数を1減らすことで実現します。
Function GetBodyRange(ByVal rng As Range) As Range
If rng.Rows.Count <= 1 Then
Set GetBodyRange = Nothing
Exit Function
End If
Set rng = rng.Offset(1)
Set rng = rng.Resize(rng.Rows.Count - 1)
Set GetBodyRange = rng
End Function
データ行が存在しない場合(範囲が1行のみの場合)はNothingを返すため、エラーを防げます。
ヘッダー除外の仕組みは、以下の記事で解説しています。
【GetColumnData関数】
データ範囲から特定の列だけを取り出す関数です。Intersectメソッドを使って、データ範囲全体と指定列の交差部分を取得します。
Function GetColumnData(dataRange As Range, colNum As Long) As Range
Dim ws As Worksheet
Set ws = dataRange.Worksheet
Set GetColumnData = Intersect(dataRange, ws.Columns(colNum))
End Function
例えば、A1からE10のデータ範囲があるとき、列番号3を指定すればC1からC10が取得できます。
Intersectメソッドの使い方は、以下の記事で解説しています。
【GetVisibleCells関数】
範囲から可視セル(表示されているセル)のみを取得する関数です。SpecialCellsメソッドを使います。
Function GetVisibleCells(rng As Range) As Range
On Error Resume Next
Set GetVisibleCells = rng.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End Function
すべてのセルが非表示の場合、SpecialCellsはエラーを発生させるため、On Error Resume Nextでエラーを無視してNothingを返すようにしています。
実際の使用例
実際に動作を確認できるコードを用意しました。新しいExcelブックを開いて、Alt + F11でVBエディタを起動し、標準モジュールに以下のコードを貼り付けて実行してみてください。
Function GetColumnData(dataRange As Range, colNum As Long) As Range
Dim ws As Worksheet
Set ws = dataRange.Worksheet
Set GetColumnData = Intersect(dataRange, ws.Columns(colNum))
End Function
Function GetBodyRange(ByVal rng As Range) As Range
If rng.Rows.Count <= 1 Then
Set GetBodyRange = Nothing
Exit Function
End If
Set rng = rng.Offset(1)
Set rng = rng.Resize(rng.Rows.Count - 1)
Set GetBodyRange = rng
End Function
Function GetVisibleCells(rng As Range) As Range
On Error Resume Next
Set GetVisibleCells = rng.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End Function
Sub TestCombinedFunctions()
Dim ws As Worksheet
Dim dataRange As Range
Dim colData As Range
Dim bodyData As Range
Dim visibleData As Range
Dim i As Long
Set ws = ActiveSheet
'サンプルデータを作成
ws.Range("A1:D1").Value = Array("ID", "名前", "年齢", "住所")
For i = 2 To 10
ws.Cells(i, 1).Value = i - 1
ws.Cells(i, 2).Value = "氏名" & i - 1
ws.Cells(i, 3).Value = 20 + i
ws.Cells(i, 4).Value = "東京都"
Next i
'4行目と7行目を非表示にする
ws.Rows(4).Hidden = True
ws.Rows(7).Hidden = True
'データ範囲全体を設定
Set dataRange = ws.Range("A1:D10")
'1. C列(年齢列)のデータ部を取得
Set colData = GetColumnData(dataRange, 3)
'2. ヘッダーを除外
Set bodyData = GetBodyRange(colData)
'3. 可視セルのみを取得
Set visibleData = GetVisibleCells(bodyData)
If Not visibleData Is Nothing Then
'可視セルに色を付ける
visibleData.Interior.Color = RGB(255, 255, 200)
MsgBox "可視セル範囲: " & visibleData.Address(False, False)
Else
MsgBox "取得できる可視セルがありません"
End If
End Sub
このコードを実行すると、A1からD10の範囲にサンプルデータが作成され、4行目と7行目が非表示になります。そして、C列のヘッダーを除いた可視セルだけ(C3、C5、C6、C8、C9、C10)に黄色い色が付きます。
処理の流れを見ていくと、まずGetColumnDataでC列全体(C1:C10)を取得し、次にGetBodyRangeでヘッダーを除外(C2:C10)し、最後にGetVisibleCellsで非表示行を除外した可視セルだけが残ります。
組み合わせパターン
この3つの関数は、状況に応じて自由に組み合わせられます。いくつかのパターンを紹介します。
【パターン1: 特定列の可視データのみ(ヘッダー含む)】
ヘッダーも含めて、特定列の可視セルだけが欲しい場合です。
Set colData = GetColumnData(dataRange, 3)
Set visibleData = GetVisibleCells(colData)
GetBodyRangeを呼ばないことで、ヘッダー行も含めた可視セルが取得できます。
【パターン2: 特定列のデータ部のみ(非表示含む)】
非表示行も含めて処理したい場合です。
Set colData = GetColumnData(dataRange, 3)
Set bodyData = GetBodyRange(colData)
GetVisibleCellsを呼ばないことで、非表示行も含めたデータ部が取得できます。フィルタがかかっていても、すべてのデータを処理したい場合に使えます。
【パターン3: 全列の可視データのみ(ヘッダー除く)】
特定列ではなく、全列のデータを対象にしたい場合です。
Set bodyData = GetBodyRange(dataRange)
Set visibleData = GetVisibleCells(bodyData)
GetColumnDataを呼ばないことで、すべての列を対象にできます。
このように、必要な処理だけを組み合わせることで、様々な要件に対応できます。
注意点
【Nothingチェックを忘れずに】
各関数は、条件に合う範囲が存在しない場合にNothingを返します。そのため、次の処理に進む前に必ずチェックが必要です。
3つの関数を組み合わせて使う場合、統合関数を作成することで、チェック処理をまとめて管理できます。
Function GetVisibleColumn(ByVal dataRange As Range, _
ByVal colNum As Long, _Optional ByRef errMsg As String) As Range
Set GetVisibleColumn = Nothing
Dim rng As Range
Set rng = GetColumnData(dataRange, colNum)
If rng Is Nothing Then
errMsg = "列" & colNum & "がデータ範囲外です"
Exit Function
End If
Set rng = GetBodyRange(rng)
If rng Is Nothing Then
errMsg = "データ行がありません"
Exit Function
End If
Set rng = GetVisibleCells(rng)
If rng Is Nothing Then
errMsg = "可視セルがありません"
Exit Function
End If
Set GetVisibleColumn = rng
End Function
この統合関数を使えば、呼び出し側はシンプルになります。
Sub TestWithErrorMessage()
Dim result As Range
Dim errMsg As String
Set result = GetVisibleColumn(ActiveSheet.Range("A1:D10"), 3, errMsg)
If result Is Nothing Then
MsgBox "エラー: " & errMsg
Else
result.Interior.Color = RGB(255, 255, 200)
End If
End Sub
Optional ByRef引数を使うことで、エラーが発生した段階とその内容を呼び出し側で確認できます。
【さらなる関数化の可能性】
責任の分離をさらに進めるなら、If rng Is Nothing Thenのチェック処理自体も関数化できます。
Function IsValidRange(ByVal rng As Range, _
ByVal errMsg As String, ByRef outMsg As String) As Boolean
IsValidRange = True
If Not (rng Is Nothing) Then Exit Function
outMsg = errMsg
IsValidRange = False
End Function
Function GetVisibleColumn(ByVal dataRange As Range, _
ByVal colNum As Long, Optional ByRef errMsg As String) As Range
Set GetVisibleColumn = Nothing
Dim rng As Range
Set rng = GetColumnData(dataRange, colNum)
If Not IsValidRange(rng, _
"列" & colNum & "がデータ範囲外です", errMsg) Then Exit Function
Set rng = GetBodyRange(rng)
If Not IsValidRange(rng, _
"データ行がありません", errMsg) Then Exit Function
Set rng = GetVisibleCells(rng)
If Not IsValidRange(rng, _
"可視セルがありません", errMsg) Then Exit Function
Set GetVisibleColumn = rng
End Function
このように関数化すると、繰り返しのチェック処理が1行で済み、コードがすっきりします。ただし、この程度の処理であれば、過度な関数化は逆に可読性を下げる可能性もあるため、プロジェクトの規模や方針に応じて判断するのがおすすめです。
【処理の順序を意識する】
関数を組み合わせる順序によって、結果が変わる場合があります。例えば「ヘッダーを除外してから列を絞り込む」のと「列を絞り込んでからヘッダーを除外する」のでは、同じ結果になりますが、可読性(読みやすさ)が変わります。
一般的には「広い範囲から徐々に絞り込む」という流れの方が、処理の意図が分かりやすくなります。つまり「全体→特定列→ヘッダー除外→可視セル」という順序です。
SpecialCellsメソッドは、フィルタで非表示になっているセルも、手動で非表示にしたセルも、どちらも「非表示」として扱います。そのため、この関数はフィルタがかかった状態でも使えます。
まとめ
3つの関数を組み合わせることで、データ範囲の絞り込み処理を柔軟に実装できます。各関数が単一の責任を持つため、コードが読みやすく、再利用もしやすくなります。