Excel VBA 逆引き集 | 実務テンプレ完全版(超再利用部品) – マッピング設定だけで動く高速ツール

Excel VBA
スポンサーリンク

ねらい:コードをいじらず「マッピング表」だけで動く汎用高速ツール

毎回「この列をあっちの列に」「この値はこう変換して」と、VBAの中身を書き換えていませんか。
それをやっている限り、ツールは作る人にしか触れないし、修正のたびにあなたが呼ばれます。

ここで目指すのは、こういう世界です。

どのシートの、どの列から、どのシートの、どの列へ写すか。
そのとき、どんな変換(トリム・全角半角・コード変換など)をかけるか。

これを「マッピング設定シート」に書くだけで、
VBAのコードは一切触らずに、高速に処理が走る“汎用エンジン”を作ることです。

「マッピングを変えれば別の業務にもそのまま流用できる」
これが“超再利用部品”としての価値です。


設計の全体像:設定シート+汎用エンジンという二段構え

シート構成のイメージ

最低限、次の3つを用意します。

Config シート
マッピング設定を書くシート。ここが“頭脳”です。

Source シート
元データが入っているシート。CSVを貼るでも、DBから持ってくるでもOK。

Target シート
マッピングに従って整形された結果を出すシート。

VBA側は、「Config を読んで、Source から Target にコピー・変換する」汎用エンジンだけを持ちます。
業務ごとの違いは、Config の中身だけで表現します。


マッピング設定シートの設計

Config シートの列構成

Config シートに、次のような列を用意します。

A列:有効フラグ(Y の行だけ処理する)
B列:SourceSheet(元シート名)
C列:SourceCol(元の列番号または列記号)
D列:TargetSheet(出力シート名)
E列:TargetCol(出力列番号または列記号)
F列:Transform(変換関数名。空ならそのままコピー)

例えば、こんな感じです。

ABCDEF
YSourceATargetATrimText
YSourceBTargetBNormalize_ZenHan
YSourceCTargetCMap_CodeToName
NSourceDTargetD

この1行が「1つのマッピングルール」です。
「Source の B列を Target の B列にコピーするとき、Normalize_ZenHan をかける」といった意味になります。


変換関数群(Transform)のテンプレ

1セルを変換する小さな関数を用意する

変換はすべて「1セルを受け取って、変換後の1セルを返す」関数として作ります。
これをいくつか用意しておき、Config の F列に名前を書くだけで呼び分けます。

例:前後スペース削除

' ModTransform.bas
Option Explicit

Public Function TrimText(ByVal s As Variant) As Variant
    If IsError(s) Then
        TrimText = s
    ElseIf IsNull(s) Or s = "" Then
        TrimText = s
    Else
        TrimText = Trim$(CStr(s))
    End If
End Function
VB

例:全角英数字 → 半角

Public Function Normalize_ZenHan(ByVal s As Variant) As Variant
    If IsError(s) Then
        Normalize_ZenHan = s
    ElseIf IsNull(s) Or s = "" Then
        Normalize_ZenHan = s
    Else
        Normalize_ZenHan = StrConv(CStr(s), vbNarrow)
    End If
End Function
VB

例:コード → 名称(簡易マスタマッピング)

ここでは、簡単のために Select Case で書きますが、実務では別シートのマスタを Dictionary 化して呼ぶ形にしてもOKです。

Public Function Map_CodeToName(ByVal s As Variant) As Variant
    Dim code As String
    If IsError(s) Then
        Map_CodeToName = s
        Exit Function
    End If
    
    code = CStr(s)
    Select Case code
        Case "01": Map_CodeToName = "東京"
        Case "02": Map_CodeToName = "大阪"
        Case "03": Map_CodeToName = "名古屋"
        Case Else: Map_CodeToName = "#未定義:" & code
    End Select
End Function
VB

ポイントは、「Transform 関数は全部同じインターフェース(引数1つ、戻り値1つ)」に揃えることです。
これで、汎用エンジンからは「名前だけ指定して呼ぶ」ことができます。


汎用エンジン:Config を読んで一気にコピー・変換する

マッピング設定を読み込む構造体

まず、Config の1行をメモリに持つための構造体を定義します。

' ModMappingEngine.bas
Option Explicit

Private Type MapRow
    Enabled As Boolean
    SourceSheet As String
    SourceCol As Long
    TargetSheet As String
    TargetCol As Long
    TransformName As String
End Type
VB

列記号(A,B,C…)で書かれている場合も扱えるように、列番号に変換する関数も用意します。

Private Function ColToNumber(ByVal colRef As Variant) As Long
    If IsNumeric(colRef) Then
        ColToNumber = CLng(colRef)
    Else
        ColToNumber = Range(CStr(colRef) & "1").Column
    End If
End Function
VB

Config シートからマッピング配列を作る

Private Function LoadMappings() As Variant
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Worksheets("Config")
    
    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
    If lastRow < 2 Then
        LoadMappings = Empty
        Exit Function
    End If
    
    Dim data As Variant
    data = ws.Range(ws.Cells(2, 1), ws.Cells(lastRow, 6)).Value
    
    Dim maps() As MapRow
    ReDim maps(1 To UBound(data, 1))
    
    Dim i As Long
    For i = 1 To UBound(data, 1)
        maps(i).Enabled = (UCase$(CStr(data(i, 1))) = "Y")
        maps(i).SourceSheet = CStr(data(i, 2))
        maps(i).SourceCol = ColToNumber(data(i, 3))
        maps(i).TargetSheet = CStr(data(i, 4))
        maps(i).TargetCol = ColToNumber(data(i, 5))
        maps(i).TransformName = CStr(data(i, 6))
    Next
    
    LoadMappings = maps
End Function
VB

ここで、「Config の2行目以降」を MapRow の配列に変換しています。
Enabled が False の行は、後でスキップします。

高速処理のための“お作法”を挟む

画面更新や再計算を止める共通処理を用意しておきます。

Private Sub SpeedOn()
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Application.Calculation = xlCalculationManual
End Sub

Private Sub SpeedOff()
    Application.Calculation = xlCalculationAutomatic
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub
VB

メインエンジン:マッピングに従って行単位で処理する

ここが心臓部です。

Public Sub RunMappingEngine()
    Dim maps As Variant
    maps = LoadMappings()
    If IsEmpty(maps) Then
        MsgBox "Configにマッピングがありません。", vbInformation
        Exit Sub
    End If
    
    SpeedOn
    
    Dim i As Long
    Dim maxRow As Long
    maxRow = GetMaxRowAcrossSources(maps)
    
    Dim r As Long
    For r = 2 To maxRow
        For i = LBound(maps) To UBound(maps)
            If maps(i).Enabled Then
                ApplyOneMapping maps(i), r
            End If
        Next i
    Next r
    
    SpeedOff
    MsgBox "マッピングエンジンの処理が完了しました。", vbInformation
End Sub
VB

ここでやっていることはシンプルです。

Config からマッピング配列を読み込む。
全マッピングの中で「最大の行数」を求める(GetMaxRowAcrossSources)。
1行ずつ、全マッピングを回して「Source から Target へコピー・変換」する。

GetMaxRowAcrossSources は、各 SourceSheet の最終行を見て、その最大値を返す関数です。

Private Function GetMaxRowAcrossSources(ByVal maps As Variant) As Long
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    
    Dim i As Long
    For i = LBound(maps) To UBound(maps)
        If maps(i).Enabled Then
            If Not dict.Exists(maps(i).SourceSheet) Then
                dict.Add maps(i).SourceSheet, 0
            End If
        End If
    Next
    
    Dim ws As Worksheet
    Dim sheetName As Variant
    Dim maxRow As Long
    maxRow = 0
    
    For Each sheetName In dict.Keys
        Set ws = ThisWorkbook.Worksheets(CStr(sheetName))
        Dim r As Long
        r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
        If r > maxRow Then maxRow = r
    Next
    
    GetMaxRowAcrossSources = maxRow
End Function
VB

1つのマッピングを1行分適用する処理

Private Sub ApplyOneMapping(ByRef m As MapRow, ByVal rowIndex As Long)
    Dim wsS As Worksheet, wsT As Worksheet
    Set wsS = ThisWorkbook.Worksheets(m.SourceSheet)
    Set wsT = ThisWorkbook.Worksheets(m.TargetSheet)
    
    Dim srcVal As Variant
    srcVal = wsS.Cells(rowIndex, m.SourceCol).Value
    
    Dim outVal As Variant
    If m.TransformName <> "" Then
        outVal = Application.Run(m.TransformName, srcVal)
    Else
        outVal = srcVal
    End If
    
    wsT.Cells(rowIndex, m.TargetCol).Value = outVal
End Sub
VB

ここが「マッピング設定だけで動く」ポイントです。

どのシートのどの列から、どのシートのどの列へ。
どの変換関数を通すか。

それを MapRow が全部持っているので、
エンジン側は「その通りに動くだけ」です。


例題:顧客CSVを貼るだけで「整形済み顧客マスタ」を作る

想定する元データ(Source)

Source シートに、システムから出力された顧客CSVを貼るとします。

A列:顧客コード(前後スペースあり)
B列:顧客名(全角半角混在)
C列:地域コード(01,02,03…)

Target シートには、次のように出したいとします。

A列:顧客コード(Trim済み)
B列:顧客名(全角→半角)
C列:地域名(コード→名称)

Config シートはこう書きます。

ABCDEF
YSourceATargetATrimText
YSourceBTargetBNormalize_ZenHan
YSourceCTargetCMap_CodeToName

あとは、Source にCSVを貼って、RunMappingEngine を実行するだけです。
コードは一切触りません。


重要ポイントの深掘り:この型を“本当に現場で使える”ようにする工夫

「行単位」ではなく「配列一括処理」にするとさらに高速

今のエンジンは「行×マッピング」の二重ループで、セルを直接読んで書いています。
数万行を超えると、ここがボトルネックになります。

本気で高速化するなら、

Source シートごとに UsedRange を配列に読み込む。
Target シートごとに配列を用意して、そこに書き込む。
最後に配列を一括でシートに書き戻す。

という構造に変えると、一気に速くなります。
ただし、コードは少し複雑になるので、まずは「行単位版」でロジックを固めてから、
必要に応じて配列版に進化させるのがおすすめです。

Transform を「設定表で切り替えられる」ことの威力

TransformName を Config に書くだけで、変換ロジックを差し替えられる、というのはかなり強力です。

TrimText を TrimTextPlus に変えたい。
Normalize_ZenHan を Normalize_ZenHanKanaOnly に変えたい。

こういう変更が、「VBAを開かずに」Config の F列を書き換えるだけで済みます。
現場の担当者にとっても、「ここを変えれば動きが変わる」と目に見える形になるので、
ツールが“ブラックボックス化”しにくくなります。

マッピング設定の「検証」をどうするか

Config にミスがあると、当然おかしな結果になります。

存在しないシート名を書いてしまう。
列記号を間違える。
TransformName をタイプミスする。

これを防ぐために、

RunMappingEngine の最初に「Config の妥当性チェック」を入れる。
存在しないシート名・列・関数名があれば、処理を止めてメッセージを出す。

という“前提条件チェック”をテンプレに組み込んでおくと、現場での事故が減ります。


まとめ:マッピング設定だけで動く高速ツールは「Config × Transform × Engine」の三位一体

このテンプレの本質は、次の三つです。

Config シートに「どこからどこへ、どう変換するか」を全部書く。
Transform モジュールに「1セルをどう変換するか」の小さな関数を並べる。
Engine モジュールは「Config を読み、Transform を呼びながら Source → Target を埋める」だけに徹する。

この型を一度作ってしまえば、

顧客マスタ整形
商品マスタ整形
CSV取り込み後の前処理
システム間インターフェースの項目マッピング

など、列構成が違うだけの仕事は、Config を変えるだけで次々にこなせます。

タイトルとURLをコピーしました