t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA クラスモジュールを使って2つの表を比較するコードをスッキリ記述する。

Excelで2つの表を比較したいというケースがよくある。
たとえば、以下のように表Aと表Bがあり、それぞれ片側にしかないIDを抽出したい。
f:id:t-hom:20151024232202p:plain

このとき、IDは重複がなく、昇順に並んでいるものとする。

VLookupでやれば簡単だが、表の規模が大きく、頻繁に行う場合はマクロ化しておくと早い。

まず、動的配列に入れて、ふつうに比較してみたのがこちら。

Sub 表比較_通常版()
    '表のデータ範囲を二次元配列に格納
    Dim 表A: 表A = Sheets(1).Range("B5:C13")
    Dim 表B: 表B = Sheets(1).Range("E5:F17")
    
    '表配列を比較
    cntA = 1: cntB = 1
    Do While cntA <= UBound(表A, 1) And cntB <= UBound(表B, 1)
        If 表A(cntA, 1) = 表B(cntB, 1) Then
            cntA = cntA + 1
            cntB = cntB + 1
        ElseIf 表A(cntA, 1) > 表B(cntB, 1) Then
            Debug.Print "表Bだけに存在 =>"; 表B(cntB, 1)
            cntB = cntB + 1
        ElseIf 表A(cntA, 1) < 表B(cntB, 1) Then
            Debug.Print "表Aだけに存在 =>"; 表A(cntA, 1)
            cntA = cntA + 1
        End If
    Loop
    
    '上記Loopの片側が終了してしまった場合の後処理
    Do While cntA <= UBound(表A, 1)
        Debug.Print "表Aだけに存在 =>"; 表A(cntA, 1)
        cntA = cntA + 1
    Loop
    Do While cntB <= UBound(表B, 1)
        Debug.Print "表Bだけに存在 =>"; 表B(cntB, 1)
        cntB = cntB + 1
    Loop
End Sub

実行するとイミディエイトウインドウに以下のように表示される。

表Bだけに存在 => 66 
表Bだけに存在 => 69 
表Aだけに存在 => 70 
表Aだけに存在 => 72 
表Bだけに存在 => 74 
表Aだけに存在 => 76 
表Bだけに存在 => 78 
表Bだけに存在 => 81 
表Bだけに存在 => 87 
表Bだけに存在 => 88 

表A、表Bそれぞれに対応する配列を作成し、cntA、cntBで比較対象行を指し示すしているのだが、いかんせん配列の扱いはごちゃごちゃして分かりづらい。

そこで、クラスモジュールを使ってこれを少しスッキリさせてみる。

クラスモジュールの名前は表としておく。
コードはこちら。

Dim Table As Variant
Dim cnt As Long
DimAs Long

Private Sub Class_Initialize()
    cnt = 1
End Sub

Sub Init(セル範囲 As Range, キー列)
    Table = セル範囲
    列 = キー列
End Sub

Property Get Value() As Variant
    Value = Table(cnt,)
End Property

Function 範囲内() As Boolean
    範囲内 = cnt <= UBound(Table)
End Function

Sub 進める()
    If cnt <= UBound(Table) Then cnt = cnt + 1
End Sub

このクラスモジュールには、表を格納するTable変数、現在行の値を返すValueプロパティ、行を進める「進める」プロシージャ、現在行が表の範囲内かどうかを返す「範囲内」関数といった機能を持たせている。

このクラスモジュール「表」を用いたコードがこちら。

Sub 表比較_オブジェクト版()
    Dim 表A As New 表: 表A.Init Sheets(1).Range("B5:C13"), 1
    Dim 表B As New 表: 表B.Init Sheets(1).Range("E5:F17"), 1
    Do While 表A.範囲内 And 表B.範囲内
        If 表A.Value = 表B.Value Then
            表A.進める
            表B.進める
        ElseIf 表A.Value > 表B.Value Then
            Debug.Print "表Bだけに存在 =>"; 表B.Value
            表B.進める
        ElseIf 表A.Value < 表B.Value Then
            Debug.Print "表Aだけに存在 =>"; 表A.Value
            表A.進める
        End If
    Loop
    Do While 表A.範囲内
        Debug.Print "表Aだけに存在 =>"; 表A.Value
        表A.進める
    Loop
    Do While 表B.範囲内
        Debug.Print "表Bだけに存在 =>"; 表B.Value
        表B.進める
    Loop
End Sub

まず表オブジェクトを作成し、Initプロシージャでセル範囲とキー列をセットする。
※キー列は読み込んだ表の左から何列目を比較に使用するかを表す値。
後は表の範囲内で値を比較してどちらを進めるかという処理になる。

わずらわしい二次元配列の範囲チェックやカウント変数の管理、値の位置管理などがオブジェクト内に隠蔽されたことで、本体コードはスッキリとシンプルにまとまっている。これがオブジェクト指向の強力さである。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。