【簡単エクセル/Excel VBA マクロ】Mac環境でデータの重複を検出する方法|配列活用|ワンポイントテクニック #014

アフィリエイト広告を利用しています。
【QRコード】PC<-->スマホの切り替えにご利用ください
アイキャッチ画像
運営者・ポテ

いつもありがとうございます!

ノンプログラマー向け「Excel VBA マクロ ワンポイントテクニック解説シリーズ」へようこそ。

本稿では「Mac環境でデータの重複を検出する方法」を解説いたします。

Information
  • ノンプログラマー
    プログラミングを専門にしていない人たちのことです。
  • VBAとマクロの違い
    VBAは、Visusal Basic for Application の略で、プログラミング言語のことです。マクロは、VBAを使って作成される「機能」のことです。

以前の記事で、Dictionary オブジェクトを使用してデータの重複を検出する方法をご紹介しました。

重複データの検出と言えば、Dictionary オブジェクトを使うのが定番です。コードもシンプルに書けますし、スマートに仕上がります。

しかし、ひとつ問題があります。

Dictionary オブジェクトは、Mac環境では使用できないのです。

これは、Scripting.Dictionary が Windows に搭載されている「Windows Script Host(WSH)」という仕組みに依存しているためで、Mac 版 Excel ではサポートされていません。

では、Mac ユーザーはどのようにすればよいのでしょうか。

答えは、Dictionary を使わずに、ローレベルな配列操作だけで実装することです。

そこで本稿では、配列だけを使って重複データを検出する方法を、2つのアプローチでご紹介します。どちらも Dictionary を使わないため、Mac でも安心して動作する構成になっています。

本稿が、あなたのVBAマクロの価値をさらに高める一助となれば幸いです。VBAを活用して、自分自身や身近なコミュニティに合ったアプリケーションを作成し、仕事量は半分に、成果は2倍にしていきましょう!成果を増やす!是非ご覧ください。

Macで重複データを検出する2つの方法

DetectDuplicate1:出てきた値をひとつずつ記録しながら、重複していないかチェックする方法

このマクロは、値を一つひとつ「記録しておく箱(配列)」に入れておき、あとから同じ値が出てきたら「おっ、さっきもあったね」と判断する仕組みです。すでにある値には、何行目に出てきたかをカンマ区切りでメモしていきます。記録しながら比較するので、処理が速く、効率的です。

DetectDuplicate2:すべてのデータをひとつずつ、他のすべてと見比べる方法

こちらは、たとえるなら「全員に自己紹介をして、同じ名前の人がいないか全員に聞いて回る」ような方法です。まず1行目の値を2行目以降すべてと比べ、次に2行目を3行目以降と比べ…という風に、全データを一つずつ総当たりで比較していきます。こちらは、DetectDuplicate1と比べると、やや処理時間がかかる仕組みですが、気にならないレベルです。仕組みがシンプルで、処理の流れが直感的に理解しやすいのが特長です。

具体例と解説

シナリオ

ここでは、「A列に入力された名前の中から、重複しているものを検出したい」という場面を想定します。次のような表を例にとります。

このような一覧がシートに入力されていて、「Tanaka」や「Sato」のように複数回登場している名前を検出し、それぞれが何行目に現れたかを把握したい、というケースです。

本記事では、このような重複検出を 配列を使った2つの異なる方法で実装し、それぞれのコード構造と動作の違いを解説していきます。

DetectDuplicate1:ユニーク配列方式

このコードは、ユニーク(一意)な値とそれに対応する行番号をそれぞれ別の配列に保持することで、効率よく重複を追跡する方法です。

処理の流れ

  • まず、A列のデータをまとめて配列に読み込みます。
    セルを1つずつ処理するのではなく、いったんすべてを読み込んで、 その後は配列だけを使って処理を進めることで、高速化を図っています。
  • 1行ずつ順番に見ていき、「すでに出てきた値かどうか」を調べます。
    過去に出てきた値(=記録済みの値)であれば、その出現行をメモしておきます。はじめての値であれば、ユニークな値として新しく登録します。
  • すでに登録済みの値には、「何行目に登場したか」をカンマ区切りで追記していきます。
    たとえば「banana」という値が3行目と5行目にあった場合、"3,5" というふうに記録されていきます。
  • 最後に、出現行が複数あるもの(=カンマが含まれるもの)だけを「重複あり」とみなして出力します。
    これにより、「1回しか登場しなかった値」は除外されます。

コードと解説

コードを示します。

Option Explicit


Sub DetectDuplicate1()

    
    ' 変数宣言
    Dim wb                   As Workbook
    Dim ws                   As Worksheet
    Dim last_row             As Long         ' データの最終行
    Dim data_rng             As Range        ' データ範囲
    Dim data_arr             As Variant      ' データ範囲を格納する配列
    Dim data_arr_row         As Long         ' データ配列用行インデックス
    Dim current_value        As String       ' 現在処理中の値
    Dim already_recorded     As Boolean      ' 値が登録済みかを判定するフラグ
    Dim unique_values_arr()  As String       ' ユニーク値格納用配列
    Dim rows_arr()           As String       ' 「行」格納用配列
    Dim unique_value_arr_idx As Long         ' ユニーク値格納用配列のインデックス
    Dim unique_count         As Long         ' 登録済み値の個数
    
    
    ' ワークブックとワークシートを取得する
    Set wb = ThisWorkbook
    Set ws = wb.Worksheets(1)
    
    ' データの最終行を取得
    last_row = ws.Cells(ws.Cells.Rows.Count, 1).End(xlUp).Row

    ' 対象データを配列に読み込む(二次元配列)
    Set data_rng = ws.Range(ws.Cells(2, 1), ws.Cells(last_row, 1))
    data_arr = data_rng.Value
    
    ' ユニーク値・行格納用配列を初期化
    ReDim unique_values_arr(1 To 1)
    ReDim rows_arr(1 To 1)
    unique_count = 0
    
    ' データ配列の行をループ
    For data_arr_row = 1 To UBound(data_arr, 1)
        
        ' 現在値を変数に格納
        current_value = CStr(data_arr(data_arr_row, 1))
        
        ' 値が登録済みかを判定するフラグを初期化
        already_recorded = False
        
        ' ユニーク値配列をループ
        For unique_value_arr_idx = 1 To unique_count
            
            ' ユニーク値配列に登録済みであれば(配列の値と現在値が一致したら)
            If unique_values_arr(unique_value_arr_idx) = current_value Then
                
                ' 「行」配列に行番号を追加
                ' ※データ範囲がA2からなので "data_arr_row +1"
                rows_arr(unique_value_arr_idx) = _
                    rows_arr(unique_value_arr_idx) & "," & (data_arr_row + 1)
                    
                already_recorded = True
            
                Exit For
                
            End If
        
        Next unique_value_arr_idx
            
        ' ユニーク値配列に登録されていなければ
        If Not already_recorded Then
        
            ' 登録済みの値の個数をインクリメント
            unique_count = unique_count + 1
                            
            ' 配列のサイズを調整
            ReDim Preserve unique_values_arr(1 To unique_count)
            ReDim Preserve rows_arr(1 To unique_count)
            
            ' 配列に値と行を格納
            unique_values_arr(unique_count) = current_value
            rows_arr(unique_count) = CStr(data_arr_row + 1)
            
        End If
    
    Next data_arr_row
    
    ' 重複情報を出力
    For unique_value_arr_idx = 1 To unique_count
             
        ' 行番号情報に","を含んでいたら
        If rows_arr(unique_value_arr_idx) Like "*,*" Then
        
            Debug.Print unique_values_arr(unique_value_arr_idx) & _
                " " & _
                rows_arr(unique_value_arr_idx)
        
        End If
         
    Next unique_value_arr_idx
    
End Sub

このコードを実行すると、次の結果が返ります。

DetectDuplicate1実行結果

運営者・ポテ

解説していきます!

Option Explicit

ここでは、Option Explicit を有効にしています。この設定を使うと、変数を使用する前に必ず宣言が必要になります。これにより、変数のタイプミスや未定義変数によるエラーを防ぎ、コードの安全性と信頼性を高めることができます。

    ' 変数宣言
    Dim wb                   As Workbook
    Dim ws                   As Worksheet
    Dim last_row             As Long         ' データの最終行
    Dim data_rng             As Range        ' データ範囲
    Dim data_arr             As Variant      ' データ範囲を格納する配列
    Dim data_arr_row         As Long         ' データ配列用行インデックス
    Dim current_value        As String       ' 現在処理中の値
    Dim already_recorded     As Boolean      ' 値が登録済みかを判定するフラグ
    Dim unique_values_arr()  As String       ' ユニーク値格納用配列
    Dim rows_arr()           As String       ' 「行」格納用配列
    Dim unique_value_arr_idx As Long         ' ユニーク値格納用配列のインデックス
    Dim unique_count         As Long         ' 登録済み値の個数

ここでは、コード内で使用する変数が宣言されています。変数は Dim 変数名 As データ型 の構文で宣言します。これにより、各変数は As 以降で指定したデータ型の値を格納できるようになります。

具体的には、現在のワークブックやワークシートを操作するためのオブジェクト型の変数、処理対象のデータを保持する配列、ユニーク値の登録と照合に使うインデックスやフラグなどを宣言しています。

また、コードの可読性を高めるため、変数は用途別にグループ化して記述しています。こうすることで、どの変数がどの処理に関わっているのかが一目で把握できるようになります。

なお、各データ型の意味は下表のとおりです。

データ型種類意味
Workbookオブジェクト型現在のワークブックやデータが格納されているワークブックを表すオブジェクト型です。
Worksheetオブジェクト型データが格納されているワークシートを表すオブジェクト型のデータ型です。
String文字列型ファイルパスや名前などのテキスト情報を格納する文字列型のデータ型です。
Long数値型-2,147,483,648 〜 2,147,483,647 の範囲の整数を格納できる数値型のデータ型です。
Variant汎用型任意のデータ型を格納できる柔軟なデータ型です。配列や複数の型に対応する場面で使用されます。
    ' ワークブックとワークシートを取得する
    Set wb = ThisWorkbook
    Set ws = wb.Worksheets(1)

ここでは、マクロを含んでいるワークブック(ThisWorkbook)から、最初のワークシート(インデックス番号1)を取得して、変数 ws に代入しています。

通常、Excel には複数のワークシートが存在するため、どのシートを対象に処理を行うかを明示する必要があります。インデックス 1 は、左端にあるシートを意味します。たとえば、シートの並び順が「Sheet1」「Sheet2」「Sheet3」であれば、「Sheet1」が対象になります。

このようにワークシートを変数に代入しておくことで、以降の処理では、変数 ws を通じてワークシートを操作できるようになり、毎回 Worksheets(1) と書かずに済むため、コードの可読性と保守性が向上します。

    ' データの最終行を取得
    last_row = ws.Cells(ws.Cells.Rows.Count, 1).End(xlUp).Row

ここでは、A列の最終行番号を取得しています。ws.Rows.Count はシート内の総行数(通常は 1048576)を意味し、ws.Cells(ws.Rows.Count, 1) によって、A列の最下部セルが参照されます。

そこから End(xlUp) を使って上方向に値が入力されているセルを探し、最初に見つかったセルの行番号を last_row に格納しています。

この例の場合は、last_rowには11が代入されます。

    ' 対象データを配列に読み込む(二次元配列)
    Set data_rng = ws.Range(ws.Cells(2, 1), ws.Cells(last_row, 1))
    data_arr = data_rng.Value

ここでは、A列の2行目から最終行までのデータを範囲として取得し、配列 data_arr に読み込んでいます。このように範囲を Range オブジェクトとして取得し、そこから .Value を代入することで、Excelのセル範囲を二次元配列として効率的に扱うことができます。

読み込まれる配列は、1行目がシートの2行目に、2行目がシートの3行目に対応する構造になっており、このあと出てくる処理では、「何行目か」を求める際に +1 する補正が行われます。

また、セルを1つずつ読み込むよりも、一括で配列化して処理することで、パフォーマンスが大きく向上します。これは、VBAにおける高速化の基本的な考え方のひとつです。

    ' ユニーク値・行格納用配列を初期化
    ReDim unique_values_arr(1 To 1)
    ReDim rows_arr(1 To 1)
    unique_count = 0

ここでは、ユニークな値(重複していない値)と、それに対応する行番号の一覧を格納するための2つの配列を初期化しています。

ReDim は、配列のサイズを指定して再定義する構文です。この時点ではまだ値は登録されていないため、いったん 1 To 1 の最小構成で初期化しておきます。この後、実際のデータ処理の中で必要に応じて ReDim Preserve によりサイズを拡張していくことになります。

また、unique_count はユニーク値がいくつ登録されたかをカウントするための変数で、初期値は 0 に設定されています。このカウントは、後続の処理でループ範囲や配列の添え字として活用されます。

    ' データ配列の行をループ
    For data_arr_row = 1 To UBound(data_arr, 1)
        
        ' 現在値を変数に格納
        current_value = CStr(data_arr(data_arr_row, 1))
        
        ' 値が登録済みかを判定するフラグを初期化
        already_recorded = False

ここでは、読み込んだデータ配列 data_arr の各行を、先頭から順に処理しています。UBound(data_arr, 1) は、配列の1次元目(=行)の最大インデックスを表しており、これが最終行の目安となります。

1行ずつ処理する中で、まず現在の値(1列目の内容)を current_value に文字列として格納し、あとでその値がすでに登録済みかどうかを判定するためのフラグ already_recordedFalse に初期化しています。

        ' ユニーク値配列をループ
        For unique_value_arr_idx = 1 To unique_count
            
            ' ユニーク値配列に登録済みであれば(配列の値と現在値が一致したら)
            If unique_values_arr(unique_value_arr_idx) = current_value Then
                
                ' 「行」配列に行番号を追加
                ' ※データ範囲がA2からなので "data_arr_row +1"
                rows_arr(unique_value_arr_idx) = _
                    rows_arr(unique_value_arr_idx) & "," & (data_arr_row + 1)
                    
                already_recorded = True
            
                Exit For
                
            End If
        
        Next unique_value_arr_idx

ここでは、これまでに記録されたユニーク値と、現在の値を1つずつ照合しています。

もし、すでに同じ値が登録されていれば、 その値の行番号情報(rows_arr)に、現在の行番号をカンマ区切りで追記します。なお、データの開始行が2行目なので、data_arr_row + 1 という補正を入れています。

一致する値が見つかった時点で already_recordedTrue に切り替え、Exit For によって照合ループを抜けます。

Information

※なお、unique_count = 0 のときは、この For ループは実行されません。

VBAの For ループは「開始 > 終了」の場合にスキップされる仕様になっており、最初の1件目は自動的に「新しい値」として登録されます。

        ' ユニーク値配列に登録されていなければ
        If Not already_recorded Then
        
            ' 登録済みの値の個数をインクリメント
            unique_count = unique_count + 1
                            
            ' 配列のサイズを調整
            ReDim Preserve unique_values_arr(1 To unique_count)
            ReDim Preserve rows_arr(1 To unique_count)
            
            ' 配列に値と行を格納
            unique_values_arr(unique_count) = current_value
            rows_arr(unique_count) = CStr(data_arr_row + 1)
            
        End If
    
    Next data_arr_row

ここでは、現在処理している値 current_value が、まだ一度も unique_values_arr に登録されていなかった場合にのみ実行される処理が書かれています。

まず、unique_count を1つ増やすことで、ユニーク値の登録件数を更新します。この値は、配列の要素数の管理にも使われているため、配列の再定義(ReDim Preserve) においても必須です。

ReDim Preserve は、既存の配列の中身を保持したままサイズを変更する構文です。ここでは、ユニーク値と対応する行番号を保持する2つの配列に対して、それぞれ1要素分の空きを追加しています。

最後に、現在の値と、その行番号(データ範囲は2行目から始まるため +1 して補正)を新しく確保した配列の末尾に格納しています。

Information

※この構造によって、同じ値が再び出現したときには、すでに存在しているインデックスに対して行番号だけが追記される仕組みになります。

逆に、初出の値はここで初めて登録され、ユニークな値として管理されるようになります。

    ' 重複情報を出力
    For unique_value_arr_idx = 1 To unique_count
             
        ' 行番号情報に","を含んでいたら
        If rows_arr(unique_value_arr_idx) Like "*,*" Then
        
            Debug.Print unique_values_arr(unique_value_arr_idx) & _
                " " & _
                rows_arr(unique_value_arr_idx)
        
        End If
         
    Next unique_value_arr_idx

ここでは、ユニーク値と対応する行番号の一覧から、2回以上出現した値(=重複している値)だけを取り出して出力しています。

rows_arr() にカンマ(,)が含まれているかどうかを、Like "*,*" という構文で判定し、該当するものだけを Debug.Print を使って即時ウィンドウに表示します。

カンマが含まれているということは、つまり、その値が2行以上に登場したことを意味します

この処理によって、どの値が、どの行で重複していたのかを簡単に確認できるようになります。

運営者・ポテ

以上で、ユニーク配列方式による重複検出コードの解説は終了です!ありがとうございます!

DetectDuplicate2:総当たり比較方式

このコードは、すべての行のペアを比較することで、重複データを検出する方法です。ユニーク値の配列と、その行番号を記録する配列を用意し、値が一致した組を見つけるたびに記録していきます。

処理の流れ

  • まず、A列のデータをまとめて配列に読み込みます。
    セルを1つずつ処理するのではなく、いったんすべてを読み込んで、 その後は配列だけを使って処理を進めることで、高速化を図っています。
  • データを順番に見ていき、すべての行の組み合わせを比較します。
    上から順に1行ずつ「基準行」として取り出し、その下にある行と順番に比べていきます。 同じ値が見つかった場合、それは「重複」として扱います。
  • 初めて見つかった重複値は、新たに配列に登録します。
    その際、重複していた2つの行番号(基準行と検出行)を、カンマ区切りで記録します。
  • すでに登録済みの値であれば、行番号だけを追加します。
    たとえば「apple」という値が3行目・5行目・8行目にあった場合、"3,5,8" のように記録されます。
  • 最後に、登録された重複値とその行番号の一覧を出力します。
    これにより、どの値がどの行で重複していたのかを確認できます。

コードと解説

コードを示します。

Sub DetectDuplicate2()

    ' 変数宣言
    Dim wb                        As Workbook
    Dim ws                        As Worksheet
    Dim last_row                  As Long         ' データの最終行
    Dim data_rng                  As Range        ' データ範囲
    Dim data_arr                  As Variant      ' データ範囲を格納する配列
    Dim base_row                  As Long         ' 比較元の行インデックス
    Dim check_row                 As Long         ' 比較先の行インデックス
    Dim base_value                As String       ' 現在の基準値
    Dim check_value               As String       ' 現在の検証値
    Dim duplicate_values_arr()    As String       ' 重複値格納用配列
    Dim duplicate_rows_arr()      As String       ' 重複値の行番号格納用配列
    Dim duplicate_count           As Long         ' 重複発生件数
    Dim duplicate_arr_index       As Long         ' 重複情報配列用インデックス

    ' ワークブックとワークシートを取得する
    Set wb = ThisWorkbook
    Set ws = wb.Worksheets(1)

    ' データの最終行を取得する
    last_row = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' A2から最終行までを配列に読み込む(二次元配列)
    Set data_rng = ws.Range(ws.Cells(2, 1), ws.Cells(last_row, 1))
    data_arr = data_rng.Value

    ' 重複情報格納用配列を初期化する
    ReDim duplicate_values_arr(1 To 1)
    ReDim duplicate_rows_arr(1 To 1)
    duplicate_count = 0

    ' データ配列の各行を順にチェックする
    For base_row = 1 To UBound(data_arr, 1)

        ' 基準行の値を取得する
        base_value = CStr(data_arr(base_row, 1))

        ' その後ろの行と比べる
        For check_row = base_row + 1 To UBound(data_arr, 1)

            ' 検証行の値を取得する
            check_value = CStr(data_arr(check_row, 1))

            ' 値が一致したら重複として処理する
            If base_value = check_value Then

                ' 既に登録済みかどうかを確認する
                duplicate_arr_index = 0
                For duplicate_arr_index = 1 To duplicate_count
                    If duplicate_values_arr(duplicate_arr_index) = base_value Then
                        Exit For
                    End If
                Next duplicate_arr_index

                ' 初めての重複なら配列に新規登録する
                If duplicate_arr_index > duplicate_count Then
                    duplicate_count = duplicate_count + 1
                    ReDim Preserve duplicate_values_arr(1 To duplicate_count)
                    ReDim Preserve duplicate_rows_arr(1 To duplicate_count)
                    duplicate_values_arr(duplicate_count) = base_value
                    duplicate_rows_arr(duplicate_count) = _
                        CStr(base_row + 1) & "," & CStr(check_row + 1)
                ' 既に登録済みなら行番号だけを追加する
                Else
                    duplicate_rows_arr(duplicate_arr_index) = _
                        duplicate_rows_arr(duplicate_arr_index) & "," & CStr(check_row + 1)
                End If

            End If

        Next check_row
    Next base_row

    ' 重複情報を出力する
    For duplicate_arr_index = 1 To duplicate_count
        Debug.Print duplicate_values_arr(duplicate_arr_index) & _
                    " " & duplicate_rows_arr(duplicate_arr_index)
    Next duplicate_arr_index
    

End Sub

このコードを実行すると、次の結果が返ります。前セクションのDetectDuplicate1と同じ結果です。

DetectDuplicate2の実行結果

運営者・ポテ

解説していきます!

    ' 変数宣言
    Dim wb                        As Workbook
    Dim ws                        As Worksheet
    Dim last_row                  As Long         ' データの最終行
    Dim data_rng                  As Range        ' データ範囲
    Dim data_arr                  As Variant      ' データ範囲を格納する配列
    Dim base_row                  As Long         ' 比較元の行インデックス
    Dim check_row                 As Long         ' 比較先の行インデックス
    Dim base_value                As String       ' 現在の基準値
    Dim check_value               As String       ' 現在の検証値
    Dim duplicate_values_arr()    As String       ' 重複値格納用配列
    Dim duplicate_rows_arr()      As String       ' 重複値の行番号格納用配列
    Dim duplicate_count           As Long         ' 重複発生件数
    Dim duplicate_arr_index       As Long         ' 重複情報配列用インデックス

ここでは、コード内で使用する変数が宣言されています。変数は Dim 変数名 As データ型 の構文で宣言します。これにより、各変数は As 以降で指定したデータ型の値を格納できるようになります。

具体的には、現在のワークブックやワークシートを操作するためのオブジェクト型の変数、処理対象のデータを保持する配列、比較のための行インデックス、検出された重複値とその出現行を記録する配列などを宣言しています。

また、コードの可読性を高めるため、変数は用途別にグループ化して記述しています。こうすることで、どの変数がどの処理に関わっているのかが一目で把握できるようになります。

なお、各データ型の意味は下表のとおりです。

データ型種類意味
Workbookオブジェクト型現在のワークブックやデータが格納されているワークブックを表すオブジェクト型です。
Worksheetオブジェクト型データが格納されているワークシートを表すオブジェクト型のデータ型です。
String文字列型ファイルパスやセルの値などのテキスト情報を格納する文字列型のデータ型です。
Long数値型-2,147,483,648 〜 2,147,483,647 の範囲の整数を格納できる数値型のデータ型です。
Variant汎用型任意のデータ型を格納できる柔軟なデータ型です。配列や複数の型に対応する場面で使用されます。
    ' ワークブックとワークシートを取得する
    Set wb = ThisWorkbook
    Set ws = wb.Worksheets(1)

ここでは、マクロを含んでいるワークブック(ThisWorkbook)から、最初のワークシート(インデックス番号 1)を取得して、変数 ws に代入しています。

通常、Excel には複数のワークシートが存在するため、どのシートを対象に処理を行うかを明示する必要があります。インデックス 1 は、左端にあるシートを意味します。たとえば、シートの並び順が「Sheet1」「Sheet2」「Sheet3」であれば、「Sheet1」が対象になります。

このようにワークシートを変数に代入しておくことで、以降の処理で変数 ws を通じてワークシートを操作できるようになり、何度も Worksheets(1) と書く必要がなくなり、コードの可読性や保守性が向上します。

    ' データの最終行を取得する
    last_row = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

ここでは、A列の最終行番号を取得しています。ws.Rows.Count はシート内の総行数(通常は 1048576)を意味し、ws.Cells(ws.Rows.Count, 1) によって、A列の最下部セルが参照されます。

そこから End(xlUp) を使って上方向に値が入力されているセルを探し、最初に見つかったセルの行番号を last_row に格納しています。

この例の場合は、last_rowには11が代入されます。

    ' A2から最終行までを配列に読み込む(二次元配列)
    Set data_rng = ws.Range(ws.Cells(2, 1), ws.Cells(last_row, 1))
    data_arr = data_rng.Value

ここでは、A列の2行目から最終行までのデータを範囲として取得し、配列 data_arr に読み込んでいます。このように範囲を Range オブジェクトとして取得し、そこから .Value を代入することで、Excelのセル範囲を二次元配列として効率的に扱うことができます。

読み込まれる配列は、1行目がシートの2行目に、2行目がシートの3行目に対応する構造になっており、このあと出てくる処理では、「何行目か」を求める際に +1 する補正が行われます。

また、セルを1つずつ読み込むよりも、一括で配列化して処理することで、パフォーマンスが大きく向上します。これは、VBAにおける高速化の基本的な考え方のひとつです。

    ' 重複情報格納用配列を初期化する
    ReDim duplicate_values_arr(1 To 1)
    ReDim duplicate_rows_arr(1 To 1)
    duplicate_count = 0

ここでは、重複した値とその行番号を記録するための2つの配列を初期化しています。

ReDim は配列のサイズを指定し直す命令で、1 To 1 とすることで、要素を1つだけ持つ配列として初期化されます。この段階ではまだデータは何も登録されていないため、必要最小限のサイズで始めておき、重複が見つかるたびに ReDim Preserve によってサイズを拡張していく設計になっています。

また、duplicate_count は重複が何件見つかったかをカウントするための変数で、初期値として 0 を代入しています。

    ' データ配列の各行を順にチェックする
    For base_row = 1 To UBound(data_arr, 1)

        ' 基準行の値を取得する
        base_value = CStr(data_arr(base_row, 1))

        ' その後ろの行と比べる
        For check_row = base_row + 1 To UBound(data_arr, 1)

            ' 検証行の値を取得する
            check_value = CStr(data_arr(check_row, 1))

ここでは、読み込んだデータ配列 data_arr を使って、すべての行のペアを比較するためのループ処理が始まります。

外側のループでは、base_row を1から順に動かしながら、比較の基準となる値(base_value)を取得しています。

内側のループでは、check_rowbase_row + 1 から始めることで、すでに比較済みの行との重複チェックを避けるようになっています。こうすることで、同じペアを2回比較する無駄を省きつつ、重複を一方向から効率よく検出する設計になっています。

各ループ内では、それぞれの値を文字列型(String)として変数に格納し、後続の処理で一致を確認します。

            ' 値が一致したら重複として処理する
            If base_value = check_value Then

                ' 既に登録済みかどうかを確認する
                duplicate_arr_index = 0
                For duplicate_arr_index = 1 To duplicate_count
                    If duplicate_values_arr(duplicate_arr_index) = base_value Then
                        Exit For
                    End If
                Next duplicate_arr_index

ここでは、比較対象の2つの値(base_valuecheck_value)が一致した場合に、その値がすでに重複として登録されているかどうかを確認する処理を行っています。

まず、duplicate_arr_index に 0 を代入し、続いて 1 から duplicate_count までループすることで、すでに登録された重複値の一覧(duplicate_values_arr)をチェックします。

ループ内で一致する値が見つかれば、その時点で Exit For により繰り返しを終了します。結果として、duplicate_arr_index1duplicate_count の範囲にあれば「すでに登録済み」、duplicate_count + 1 の位置まで進んだ場合は「未登録」という判定になります。

このようにして、重複の初出かどうかを判定しているのが、この処理のポイントです。

                ' 初めての重複なら配列に新規登録する
                If duplicate_arr_index > duplicate_count Then
                    duplicate_count = duplicate_count + 1
                    ReDim Preserve duplicate_values_arr(1 To duplicate_count)
                    ReDim Preserve duplicate_rows_arr(1 To duplicate_count)
                    duplicate_values_arr(duplicate_count) = base_value
                    duplicate_rows_arr(duplicate_count) = _
                        CStr(base_row + 1) & "," & CStr(check_row + 1)
                Else
                ' 既に登録済みなら行番号だけを追加する
                Else
                    duplicate_rows_arr(duplicate_arr_index) = _
                        duplicate_rows_arr(duplicate_arr_index) & "," & CStr(check_row + 1)
                End If

            End If

        Next check_row
    Next base_row

ここでは、前の判定結果に基づき、重複した値が初めて見つかったものか、すでに登録済みのものかによって処理を分けています

もし、配列にまだ登録されていない重複値であれば、duplicate_count を1つ増やし、uplicate_values_arrduplicate_rows_arr のサイズを ReDim Preserve で拡張します。そして、重複していた2つの行番号(基準行と検出行)をカンマ区切りで文字列として記録します。

一方、すでに登録されている重複値であれば、新たに見つかった行番号だけを追記します。このようにして、同じ値が登場するたびに、行番号の一覧がカンマ区切りで蓄積されていく構造になっています。

    ' 重複情報を出力する
    For duplicate_arr_index = 1 To duplicate_count
        Debug.Print duplicate_values_arr(duplicate_arr_index) & _
                    " " & duplicate_rows_arr(duplicate_arr_index)
    Next duplicate_arr_index

ここでは、登録された重複値と対応する行番号の一覧を、イミディエイトウィンドウに出力しています。

Information

※イミディエイトウィンドウ(Immediate Window)とは、VBE(Visual Basic Editor)内で Debug.Print の出力結果を確認できるウィンドウです。Ctrl + G で表示できます。

ループを回しながら、各インデックスに格納された値と、カンマで区切られた行番号の文字列を Debug.Print で表示します。

この処理によって、どの値がどの行に重複していたかを確認できるようになります。

運営者・ポテ

以上で、全組み合わせ比較方式による重複検出コードの解説は終了です!

関連記事

本稿と関連の深い記事です。もしよろしければ、合わせてご活用ください。

VBAプログラミングスキルアップのための参考情報

ここでは参考図書を紹介いたしますが、これらに限らず自分に合うものを選ぶことが重要だと考えております。皆様の、より一層のご成功を心よりお祈りしております。

VBAプログラミングのスキルアップ

学習用としてもハンドブックとしても役立つ便利な書籍がこちらです。価格はやや高めですが、その内容は非常に充実しています。相応のスキルを身に付けるためには、こうしたしっかりとした書籍を一冊持っておくと良いでしょう。



入門書に関しては、どの書籍も大きな違いはありません。あまり迷うことに時間をかけるよりは、手頃なものを一冊選んでみると良いでしょう。VBAの入門書は数多く出版されていますので、興味がある方はぜひチェックしてみてください。

甲乙つけがたい場合、私はインプレス社の「いちばんやさしい」シリーズを選ぶことが多いです。

\チェックしてみよう/

\チェックしてみよう/

\チェックしてみよう/


VBAのプログラミング能力を客観的に証明したい場合には「VBAエキスパート試験」があります。この試験はVBAの知識を公式に認定するものです。VBAの総合的な能力獲得を目指す方に適しています。以下の公式テキストが販売されております。



プログラミングの一般教養

「独学プログラマー」というプログラミングの魅力を解説した書籍があります。これはVBAではなくPythonを題材としていますが、プログラミングの基本的な知識や思考法、仕事の進め方まで幅広く学べます。


こちらの記事でも紹介しております。もしよろしければご覧ください。

QRコード
【QRコード】PC<-->スマホの切り替えにご利用ください

おわりに

運営者・ポテ

ご覧いただきありがとうございました!

今回の記事では、「Mac環境でデータの重複を検出する方法」を解説いたしました。

お問い合わせやご要望等ございましたら、「お問い合わせ/ご要望」またはコメントにて、ご連絡いただければ幸いでございます。

皆様の人生がより一層素晴らしいものになるよう、少しでもお役に立てれば幸いでございます。

なお、当サイトでは様々な情報を発信しております。もしよろしければ、トップページもご覧いただけると幸いでございます。

この記事を書いた人

運営者・ポテ
■人生を追求する凡人■日本一安全で、気の向くままに自分の時間を過ごせる、こだわりのキャンプ場を作るのが夢■光学・機械系エンジニア(歴20年、内マネジメント10年、特許数件権利化)/副業フリーランスエンジニア■読書・文学愛好■人生は時間そのもの。ひとりでも多くの人が「より良い人生にするために時間を使って欲しい」と願い、仕事のスキルの向上、余暇の充実、資産形成を研究。■VBAアプリ開発サービス提供中(業務委託 / VBA使用経験20年)■Python愛好(歴5年)■VBAエキスパート「Excel VBA スタンダード」(上級者向け資格)/ Python 3 エンジニア認定基礎(経済産業省「ITスキル標準(ITSS)」に掲載)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です