エクセルVBAでクラスモジュールを使う 【第3回】重複データを削除する
今回は、重複データ削除用のプロシージャを紹介したいと思います。
データが重複しているかを見る手順は;
データが離れたところにあると確認が大変なので、
1.ソートする項目を決めてソートする(=>重複データが上下連続する)
2.ソートした項目を上から順に読み、同じデータが連続=>1件削除
データのイメージは以下の通り。
標準モジュールのプロシージャは以下のようにしました。
Dim RowTop_ As Integer '範囲内最上行
Dim RowBottom_ As Integer '範囲内最下行
データを全件処理するために1件目、最後が何行目かの情報が必要です。1件目をRowTop_、最終行をRowBottom_として定義。東証のストップ高データの件数は多い日でも30件程度ですので、Integerを使用。
Dim Rng As Range
Range関数を使って取得したい情報がいくつかあるので、Rngの名前で定義
Dim Rec1 As Variant: Rec1 = "" '先に読むレコード
Dim Rec2 As Variant: Rec2 = "" '後に読むレコード
1件目と2件目のように2行を比較しますので、定義。: Rec1 = ""は初期値の設定です。処理の性質上、同じプロシージャ内で何回も比較処理を繰り返すことはありませんので、前回データが残っていることはありませんが、とりあえず習慣づけるために設定。
Dim i As Integer 'for~Nextのカウンター
繰り返し(ループ)処理の件数カウンターです。iに現在処理中の行が入ります。
Dim Col As Integer 'A列から数えた列番号
引数のセル名から列情報を取得したり、CurrentRegionで所得した対象範囲から重複比較したい列に限定するために.Columnや.Itemを使いますので定義
Col = Range(FldNameCell_).Column
引数として入力したセル名から取得した列番号をColに代入。A列なら1、C列なら3、E列なら5、とA列から順番になっています。
Set Rng = Range(FldNameCell_).CurrentRegion.Item(Col)
.Itemの存在は最近知りましたので、忘れないように使ってみましたCurrentRegionで取得した範囲全体を、引数入力したセルの列のみに絞り込み
RowTop_ = Rng.Row
RowBottom_ = Rng.End(xlDown).Row
今回のデータは列によって入力されている行数が異なることはありませんが、一応Rngで列を指定して行番号を取得
If Rec1 = "" And Rec2 = "" Then
Exit For
End If
この部分はなくても影響ありません。とりあえず、ありえないケースを除外するルーチンを入れておいただけです。使っているうちに、除外したいデータとかが出てきたときにIf 条件式をそれに合わせて使おうかなといった感じ
If Rec1 = Rec2 Then
Cells(i, Col).EntireRow.Delete
i = i - 1
End If
重複データが出てきた時に、そのデータを削除するルーチンで、削除した後にiカウンターを1つ戻しています。削除して処理中のレコードが1件減ったので1つ戻しています。
Rec2 = Rec1
このあとNextからFor i = RowTop_ To RowBottom_に戻ると次のレコードを読み込みますので、その前に今のレコードをその前のレコードに移して、前後で比較できるようにしています。
今回の2つのプロシージャは、引数にソートや削除したいデータの1件目レコードのセル名を入れるのではなく、項目名のセル名を入れるようにしました。その理由としては、”項目名 ≠ 処理したいレコードの中身” が必ず成り立つため、間違って削除されてしまう可能性がないからです。”項目名 = 処理したいレコードの中身” が 成り立つようなデータを処理する場合は、検証をしてルーチンを少し改良する必要が出てくると思います。
よく使う単純な処理は基本、call 汎用.プロシージャ名(引数、、、)で構成されるプロシージャをフォーム上のボタンに登録して実行できるようにして、手間を省こうと考えています。