税理士でMicrosoft MVP for Excelの井ノ上陽一さんが、一番下に合計が計算されている月ごとの交通費精算シートが存在しているとき、集計シートにその合計値をコピーするマクロをご紹介してらっしゃいます。
Sub shuukei()
Dim Shuukei_cell
Shuukei_cell = 1
Dim w As Worksheet
For Each w In Worksheets
If w.Name < > "集計" Then
Dim Last_row
Last_row = w.Range("d" & Rows.Count).End(xlUp).Row
Range("b" & Shuukei_cell).Value = w.Range("d" & Last_row)
Shuukei_cell = Shuukei_cell + 1
End If
Next
End Sub
マクロの経験がない方に、どういうコードがわかりやすいのか、考えながら書かれたのだろうな、と感じるコードです。
プログラミングの経験のない方が、Excelマクロを学習するときの障害はいろいろとあります。その一つが変数です。
変数の名前をどうするのか、変数の型をどうすればいいのか、といったことで悩んでしまう方はたくさんいらっしゃいます。
井ノ上さんが書かれたコードは、特に、型をどうすればいいのかがわからない方に向けたものだと感じました。変数の宣言は行うけれど、型宣言を省略している部分があるからです。
また、プログラミングの経験のない方がコードを読むと、それが変数なのかVBA(Visual Basic for Applications)で決められている予約語なのかがわからない、という問題も発生します。その解決策の一つとして井ノ上さんは、変数を使う直前で宣言するという方法をとってらっしゃいます。
このコードを読んで、私は型宣言がコードの読みやすさに影響を与えるということを強く実感させていただきました。また変数宣言はプロシージャの先頭で行われているほうが読みやすいということも感じました。
コードを読み進めて行く中でShuukei_cell・Last_rowという変数を見たときに、
「この変数にはRangeオブジェクトが入っているんだっけ?整数が入っているんだっけ?」
という疑問が瞬間的にわいてきたのには、かなり驚かされました。
一般に、型宣言を何のためにするのかというときに、パフォーマンスを理由として上げる方が多いと思いますが、井ノ上さんのコードを読んで、変数の型宣言は可読性を上げるために行うという面もあると実感しました。
そんなことを感じた私がこのマクロを作るとしたら、どんなコードを書くのかをご紹介させていただきます。
なお、井ノ上さんのコードは、マクロを使わずINDIRECT関数とCOUNTA関数を使って合計値を各月のシートから取得する方法からの続きで書かれているので、敢えて、シート名が事前に入力されているという前提で書かれていますが、実務上はシート名もマクロの中で書き出すほうがいいでしょうから、その部分は仕様を変更したコードにしました。
Sub 合計値を集計シートにまとめる()
Dim w As Worksheet
Dim mrg_row As Long ' 集計シートの行番号
Dim sum_row As Long ' 各月の合計の行番号
' 集計シートで書き出しを開始する行の設定
mrg_row = 1
' 集計シート以外の全シートにループ処理
For Each w In Worksheets
If w.Name <> "集計" Then
' 合計の入力されている行番号を変数に格納
sum_row = _
w.Range("D" & Rows.Count).End(xlUp).Row
' 集計シートのA列にシート名の書き出し
Worksheets("集計").Range("A" & mrg_row).Value = _
w.Name
' 集計シートのB列に合計値の書き出し
Worksheets("集計").Range("B" & mrg_row).Value = _
w.Range("D" & sum_row).Value
' 集計シートの行番号の加算
mrg_row = mrg_row + 1
End If
Next w
変数名は、
mrg_row(集計シートの行番号格納用変数)
sum_row(各月の合計の行番号格納用変数)
としました。
意味から考えると、
mrg_row_num
sum_row_num
としたいところですが、ちょっと長いかなと感じたので、mrg_row・sum_rowにしました。
mrg_row・sum_rowという名前だと、Rangeオブジェクトが格納されると誤解される可能性もありますが、型宣言As Longで整数しか入らないと明確にすることによって、その可能性を否定しています。
一般に、同じようなデータ・似たようなデータを格納する変数については、変数名の文字数を揃えておくことで可読性が上がることが多いはずです。
そのためmrg_row・sum_rowとしました。mrgはマージ(merge)の省略です。
実は、はじめのうちはsum_rowではなく、井ノ上さんのlast_rowにならって文字数をmrg_rowに合わせるためend_rowにしていましたが、意味がよりわかるsum_rowのほうがend_rowよりも相応しいと感じ、途中で変更しました。
この二つの変数
mrg_row
sum_row
を
row_mrg
row_sum
でもいいのではないかと考える方もかなりいらっしゃるでしょう。
ですが、人は最初に目に入ったものに影響を受けてしまうことがとても多いものです。
同じ「row_」で始めてしまうと、2つの変数を誤読してしまう危険性が高くなってしまいます。
そのためrow_mrg・row_sumより、mrg_row・sum_rowのほうが相応しいと考えています。
また一般にVBAでは、変数をmrgRow・sumRowのように、単語や意味の区切り部分を大文字にするキャメル記法を使って書く方が多いようです。しかし、このキャメル記法によっても初心者の方は、それが変数なのかVBAの予約語なのかがわからなくなってしまうということがあるのです。ですから、最近の私はキャメル記法をやめて「_」(アンダーバー)を使ったスネーク記法にして、全部小文字で書くようにしています。
変数については以上のようなことを考えながら書きました。
その他、以下のようなことも考えました。
Excel VBAになれた方だとCellsプロパティを使った
Cells(mrg_row, 1)
で問題ありませんがマクロ経験がない方の負荷を減らすために、普段Excelのワークシート上で見ているセル番地の指定に近い、Rangeプロパティを使った
Range("A" & mrg_row)
としています。
また、
Range("A" & mrg_row)
は、
Range("a" & mrg_row)
と小文字で書いても動作上は問題ありませんが、普段Excel上で見慣れている大文字を使うほうが、マクロ初心者の方がコードを読むときの負荷を減らすために相応しいと考えています。
井ノ上さんのマクロは集計シート上のボタンから実行することを前提としているので、データのコピーを行う処理でコピー先シート名は省略していますが、可読性を考えると冗長にはなりますが
Worksheets("集計").Range("B" & mrg_row).Value = _
w.Range("D" & sum_row).Value
のほうが、誤読の可能性が減っていいのではないかと思っています。
なお、初心者の方を主な対象としているのでここではWith句を使っていませんが、マクロに慣れた方向けには勿論With句を使って
Worksheets("集計")
.Range("A" & mrg_row).Value = _
w.Name
.Range("B" & mrg_row).Value = _
w.Range("D" & sum_row).Value
End With
としたほうがいいでしょう。
といったことをあれこれ考えながら書いたコードが上記のサンプルコードです。
「ビジネスパーソンのためのExcelマクロ入門講座」を2011年から何度も開催する中で、受講いただいたみなさんの反応を見ながら、私の書くコードはかなり変化してきました。
今回、井ノ上さんが、初心者の方に向けてどう書くべきなのか苦悩中のコードを見せていただいて、自分の考え方をより整理できたなと感じています。ありがとうございました!
(こういう往復書簡的なことをやりやすいのがブログのいいところでもあるなあ、ということも同時に思いつつ)
Home » エクセルマクロ・Excel VBAの使い方 » マクロのサンプル » 合計を集計用シートにまとめるExcelマクロ