対象レコードの任意の1項目の中に出現するすべてのデータを、重複無く抜き出します。サンプルコードは、単純集計表の行見出しを、VBAで重複無く抽出しますが、本記事のスキルを応用すれば、任意のリストから重複なくすべてのデータを抜き出すことが出来ます。
まずは本記事でご紹介するサンプルファイルの内容と、実行ビフォアアフターが分かる1分動画(音声無し)をご確認ください。
目次
VBAで同一シート上に単純集計表の行見出しを作成するサンプルファイル
では本記事で取り扱うサンプルシートの単純集計表をご紹介しますね。
集計対象のレコードは、既に本サイトで何度か使用しているサンプルです。右側の緑の枠は、行見出しに「仕入担当者」を配置した単純集計表のサンプルになります。
なおダウンロードファイルには上図赤枠内の行見出し(項目名除く担当者名)は、サンプルコード実行後に表示されます。
単純集計表とは
ここで単純集計表について簡単に触れておきますね。単純集計表とは、集計対象レコードの任意の項目について、その項目別に各レコードの値を集計して表にしたものです。
今回の例で言いますと、集計対象レコードの各項目(商品ID, 商品名など)のうち、「仕入担当」の一項目について、各担当者別に棚卸資産を集計する表ということになります。
本記事では、VBAでその単純集計表を作る過程にある、赤枠で囲まれた見出しを自動作成するところまでのサンプルコードを詳しくご紹介します。
表見出しを自動作成するVBAサンプルコード
では、今回のサンプルコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | Sub 表見出しを重複なく作成する() Dim lngERow As Long '最終レコードの行番号 Dim rngSelf As Range '表見出しの元になる範囲 Dim lngETNm As Long '表見出し(見出し重複削除前)の最終行番号 Dim r As Integer Application.ScreenUpdating = False '最終レコード行番号取得 lngERow = Range("A" & Rows.Count).End(xlUp).Row '表見出しになる範囲を取得 Set rngSelf = Range(Cells(2, 6), Cells(lngERow, 6)) '取得した表見出しの位置へコピペ rngSelf.Copy Cells(8, 10) '表見出しの並べ替え Range("J7").CurrentRegion.Sort Cells(8, 10), xlAscending, Header:=xlYes '表見出しの最終行番号取得 lngETNm = Range("J" & Rows.Count).End(xlUp).Row '重複するする見出し(担当者名)を削除 For r = lngETNm To 9 Step -1 If Cells(r, 10) = Cells(r - 1, 10) Then Cells(r, 10).Delete End If Next End Sub |
それでは、つぎにサンプルコードのアルゴリズムについて、詳しくご紹介していきますね。
表見出しを自動作成するVBAコードのアルゴリズム
2行目:Dim lngERow As Long
3行目:Dim rngSelf As Range
4行目:Dim lngETNm As Long
今回のVBAコード内で使用するおもな変数になります。VBAコード2行目、4行目はレコード最終行番号を格納する変数です。
「lngETNm」は、集計対象のレコードからコピペされた見出しデータの最終行番号を格納する変数です。
11行目: lngERow = Range("A" & Rows.Count).End(xlUp).Row
レコードの最終行番号を取得するVBAコードです。
14行目: Set rngSelf = Range(Cells(2, 6), Cells(lngERow, 6))
単純集計表に並ぶ仕入担当のデータは、集計対象レコードで出現するすべての「仕入担当」が漏れ・重複なく並ぶ必要がありますよね。そのためには、まずこのVBAコードで、集計対象レコードのすべての「仕入担当」データを取得します。(下図)
具体的には、赤枠の範囲をRangeオブジェクトとして変数「rngSelf」に設定します。今回のサンプルでは、Excelシート2行目の「田中」から始まり、Excelシート16行目の「田中」までの範囲ということになります。
ここで既に「田中」というデータが重複しているのが分かりますよね。
この段階では、重複を含んでも構わないので、とにかく集計対象すべての「仕入担当」データを、まずはRangeオブジェクト変数「rngSelf」に漏らさず設定するのがポイントになります。
17行目: rngSelf.Copy Cells(8, 10)
VBAコード14行目で設定したオブジェクト変数を、見出しの先頭行にコピペするVBAコードになります。RangeオブジェクトのCopyメソッドの使い方を含め図を用意しましたのでご覧ください。
手作業でいうと「rngSelf」の範囲を選択した状態で、右クリックして「コピー」メニューを選択します。つぎに貼付け先のCells(8, 10)を選択した状態で、右クリックして「貼付け」を選択します。これと全く同じことをこのVBAコードで実装しています。
20行目: Range("J7").CurrentRegion.Sort Cells(8, 10), xlAscending, Header:=xlYes
VBAコード17行目で表見出し先にコピペしたデータには、まだ重複が含まれています。そこで、重複するデータを削除することになるのですが、その前に張り付けたデータをソート(並べ替え)します。そのためのVBAコードになります。
ここでソートする必要性については、後ほど重複データを削除するVBAコードのところで詳しく説明します。ここではまず、重複データを削除する前にソートが必要であるとして、そのソートをVBAコードで行う方法について先にお話しします。
VBAコード 17行目 でコピペが終わった状態が図の左側になります。そして、そのコピペした後の見出しデータを、ソート(並べ替え)した結果が右側になります。
VBAコードによる並べ替えでは、「Sort」メソッドを使います。まず並べ替えを行いたいRangeオブジェクトの後ろに「.Sort」と書きます。
ここで「.Sort」メソッドで並べ替えを行う対象として、Range("J7").CurrentRegion(緑で囲まれた範囲)を指定しています。CurrentRegionプロパティは、直前のRangeオブジェクトを含む「周囲が空白行と空白列で囲まれた領域」を示します。
「.Sort」につづき、並べ替え対象の列を指定するため、並べ替えのキーとなるセル(サンプルの場合Cells(8, 10))を指定します。このキーの指定は、並べ替えるデータの最初のレコードを指定すればOKです。
続けて、「xlAscending」オプションで昇順で並べ替えるように指定しています。なお降順で並べ替える場合は、「xlDescending」を指定します。
最後の「Header:=xlYes」ですが、並べ替えを行う領域(サンプルでは緑色の領域、Range("J7").CurrentRegion)に見出し(Header)の設定が含まれているか否かを指定しています。
ここではサンプルのRange("J7").CurrentRegionには、列見出し(「仕入担当」、「棚卸資産」)が含まれていますので「xlYes(含まれる)」を指定しています。
「Sort」メソッドには、ここでお話しした以外にもいろいろなオプションがありますが、経験上本記事で紹介した「並べ替えキーの指定」、「昇順/降順指定」、「列見出しを含むか含まないか」を覚えればほとんどの集計業務に問題なく使えます。
23行目: lngETNm = Range("J" & Rows.Count).End(xlUp).Row
ここまでで並べ替えが終わりましたので、次は見出しにするための「仕入担当」データの重複削除になります。削除の方法は、「 VBAコードでワークシートの行を削除する」 記事でご紹介した行削除の考え方と同じく、データの最終行から削除します。
そこで、削除する前に対象となる最終レコードの行番号を取得するのがここで紹介するVBAコードです。ちなみに、「lngETNm」の値は、下の図のようにExcelシート22行目となります。
27~31行目: For r = lngETNm To 9 Step -1 ~ Next
For ~ Next文による繰り返し処理範囲になります。行削除のときと同様、レコード最終行から繰り返し処理を開始します。そして、今回のサンプルでは見出しの先頭レコードの1つ前のレコードで処理が終わります。
ここで、なぜ先頭レコードの1つ前のレコードで処理が終了するかについては、この後の説明で詳しくお話ししますね。
28行目: If Cells(r, 10) = Cells(r - 1, 10) Then
繰り返し処理の中にある条件判定を行うVBAコードです。Cells(r, 10)はExcelシートJ列(10列目)の各見出しデータになります。左辺の処理対象データと、右辺の処理対象データの1つ上のセル(Cells(r-1, 1))を比較しています。
For ~ Next文が先頭レコードの1つ前のセルで処理が終わっている理由は、この条件式の右辺「Cells(r-1, 10)」が理由です。仮に先頭レコードのr=8まで実行してしまうと、この時の右辺「Cells(r-1, 10)」は、Cells(7, 10)となり「仕入価格」という見出しの文字と比較してしまうからです。
29行目: Cells(r, 10).Delete
VBAコード28行目の判定条件が真の場合に、セルを削除するVBAコードになります。では、 VBAコード 28行目、29行目をまとめて図で説明しますね。

まず繰り返し処理は、lngETNmで求めたExcelシートの22行目から始まります。図の上側はr=22のときのIf文で対象にしているデータを、色付きの枠で囲っています。
r=22のとき、セルを青色で囲ったデータは If文の左辺になります。また右辺になるCells(r-1, 10)は、1つ上のセル(緑色の枠)になります。
すなわち、If文の条件式は常に上下のセルの値を比較します。そして、For ~ Next文の処理が進むにつれて、比較するセル同士も情報へと移動します。
図の下側にある表は、r=22からr=12までの処理の様子を示したものです。(スペースの都合上、r=9まで掲載しておりません)
表に書きました通り、上下のセルが同じ場合は、下側のセル(処理対象のセル)を削除します。また、上下のセルが異なる場合は削除しません。
以上のように、上下のセルが同じか異なるかというロジックで重複を判定しますので、前処理にあたるソート(並べ替え)処理が必要だったのです。
まとめ
本記事では、単純集計表の行見出しを集計対象のレコードから作成するVBAコードについて説明しました。ポイントは、集計対象のレコードあるすべてのデータから、過不足なく出現するすべてのデータを取り出すことです。
この基本的な方法は、今回のように見出しを作成するだけではなく、簡単にデータのバリエーションを確認するためのVBAコードとしても応用ができます。
例えば、ある集計対象のデータ項目の中に、想定外のものが入っていないかどうかをExcelシート上に展開して確認できます。もちろん、単なる想定外のデータ探しには、Excelのフィルタ機能でも可能です。
ただExcelシートに展開しておけば、その想定外のデータ確認作業だけでなく、後続の処理をVBAコードで処理をすることが出来ますので、今回の手法は覚えておいて損はないと思います。