Excel(VBA)では作業をする時は、どこの列の何行目から(左上のセル)どこの列の何行目まで(右下のセル)を必ず探す必要があります
しかしテーブルを使用すればその必要はもうありません
この記事ではVBAを学び始めた方でも分かる様にWithを使用せずに記述します
Withについては記事の最後に説明します
ListObject(テーブルのオブジェクト名)
ListObjectの列はListColumns(ListColumnsオブジェクト)
ListObjectの行はListRows(ListRowsオブジェクト)
ListObjectのデータ行はDataBodyRange(Range型オブジェクト)
ListObjectの全体はRange(Range型オブジェクト)
テーブルの構造
テーブル全体(テーブルすべて)
Worksheets("シート名").ListObjects("テーブル名").Range
データ行(テーブルのデータ行)(ヘッダー、集計行を除く)
Worksheets("シート名").ListObjects("テーブル名").DataBodyRange
テーブルの列の全体(ヘッダー、集計行含む)
Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前").Range
テーブルの列のデータ行(ヘッダー、集計行を除く)
Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前").DataBodyRange
テーブルのヘッダー
Worksheets("シート名").ListObjects("テーブル名").HeaderRowRange
集計行(テーブルの集計行)
Worksheets("シート名").ListObjects("テーブル名").TotalsRowRangeRange
データ行の行数を数える時は
Worksheets("シート名").ListObjects("テーブル名").DataBodyRange.Rows.Count
DataBodyRangeはRange型オブジェクトなので
(あるいはDataBodyRangeの頭文字をとってDBR)
Dim rng as Range
Set rng = Worksheets("シート名").ListObjects("テーブル名").DataBodyRange
でテーブルのデータ行をSetすることが出来ます
rng.Copyでテーブルのデータ行をコピーする事もできます
これでDataBodyRangeという、意味とスペルが分かるけど、非常に長いコードを記述する必要がなくなります
テーブルの行を削除するときは
If Not (Worksheets("シート名").ListObjects("テーブル名").DataBodyRange is Nothing) Then Worksheets("シート名").ListObjects("テーブル名").DataBodyRange.Delete
If Not (rng is Nothing) Then rng.Delete
です
rng.Dleteで削除可能ですが、Deleteを実行すると、rngが「オブジェクトが必要です」になるので、
Set rng = Worksheets("シート名").ListObjects("テーブル名").DataBodyRange
が必要な場合があります。
その後rng.rows(i).value = でテーブルの全列に対して入力が可能です(特定の列に対しては後述)
(~.Copy rng を行った後に、.Deleteを行って上記の操作を行うとエラーが発生し、~.Copy rngを行わなければ上記の操作のエラーは発生しませんでした)
テーブルに繰り返し行を追加、最終行にデータをコピーするときは
Set rng = Worksheets("シート名").ListObjects("テーブル名").ListRow.Add.Range
~.Copy rng
(これで最終行が必ず取得できる、もう二度とCells(Rows.count,列番号).End(xlup).Row(意味不明)やlastrow = lastrow + 1に悩まされる必要はない)
(ちなみに Cells(Rows.count,列番号).End(xlup) は実はRange(セル)。
.Rowでたった行番号を取得する為だけに使用するのは非常にもったいない。
set 変数名 = Cells(Rows.count,列番号).End(xlup) で利用した方が可能性が広がるのでは、、、)
(注:必ず行をAdoしてからコピーメゾットを行う。
先にコピーメゾットを行うと、行をAdoしたときにコピーモードが解除されるため、ペースト実行時にエラーが発生する。)
フィルターをかけたいときは
VBAのフィルターはフィルターを行いたい列を オートフィルターの範囲内での列の位置(番号)を整数で指定する必要があります
テーブルではWorksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).indexでフィルターしたい列の番号が簡単に取得できます
Dim c as Long
c = Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).index
Worksheets("シート名").ListObjects("テーブル名").Range.AutoFilter ~.ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).index, "フィルターしたい文字列やセル"
で簡単にテーブルにフィルターを(動的に、可変で)かけることが出来ます
(もう二度とヘッダーの範囲をmatch関数で検索して見つかった位置をFieldに代入する必要はありません)
フィルターした結果の「データ行」(可視セル、可視行)のみコピーしたいときは
上記フィルター実施後に
Worksheets("シート名").ListObjects("テーブル名").DataBodyRange.Copy
(か、Worksheets("シート名").ListObjects("テーブル名").DataBodyRange.SpecialCells(xlCellTypeVisible).Copy)
(フィルターした結果の行のみをコピーする方法として、
Range("A1").CurrentRegion.Offset(1,0).Resize(Cells(Rows.Count,1).End(xlup).Row - 1).SpecialCells(xlCellTypeVisible).Copy
でヘッダーから1行下がり、範囲を1行減らす初心者には非常に難解なコードが多いですが、テーブルにはその必要がありません)
(今はもうFILTER関数を使用していたとしても、配列にテーブル全体、含むに列全体を使用することが出来るので非常に便利です)
並び替え、Sort
Worksheets("シート名").ListObjects("テーブル名").Range.Sort Key1:=~.ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).Range, Order1:=xlAscending, Header:=xlYes
Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).Range はヘッダー含む全行が対象なので, Header:=xlYes
, Header:=xlYesは最後のキーの末尾に一つだけでいい
(私がSortを行ってもなぜか並び替えられないので他の方が参考になるかもしれません。)
配列に取り込む場合
Dim arr as Variant
arr = Worksheets("シート名").ListObjects("テーブル名").DataBodyRange
arr = Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).DataBodyRange
でテーブルのデータ行のみ配列に取り込む事が可能、当然インデックスは 1 はじまり
Match関数でテーブルの特定の列を範囲にし、何行目に値があるかを探す場合
Application.Match("検索値",Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).DataBodyRange,0)
列の名前で動的に範囲を設定することが出来ます
特定の列の特定の行に入力する場合
Worksheets("シート名").ListObjects("テーブル名").ListColumns("列の名前(ヘッダー)"(セルの値で指定可能)).DataBodyRange.Rows(i).value
Withについて
With の後ろに省略したいコードを記述することによって、End Withを記述するまでの間に限って、省略したいコードを省略することが出来ます
Dim rng as Range
(例)With Worksheets("シート名").ListObjects("テーブル名") ←以後テーブル名まで省略できる
If Not (.DataBodyRange is Nothing) Then .DataBodyRange.Delete
Set rng = .ListRow.Add.Range
ws.Range("A1:D100").Copy rng
End With
Dim wb as Workbook
Set wb = Workbooks.Open(~)
With wb.sheets(1) ←以後シートを変数に入れなくてもシートまで省略できる
.Range("A1").Copy
End With