VBA100本ノック 38本目:1シートを複数シートに振り分け
この記事から得るもの
日付データから、祝日、平日、休日の判断ができるようになる。
書込み処理のサブルーチン化のやり方が分かる。
1 今回のお題
「売上」シートのA列に日付が昇順で入っています。
土日祝と平日に分けて別シートに出力してください。
・「売上」シートの列数は不定。
・「土日祝」「平日」シートは既存です。
・祝日は「祝日」シートのA列にあります。
※セルの書式の扱いは任意
2 今回のお題の意図
Weekday関数を使って曜日を取得し、条件分岐する。
Countif関数を使って祝日の、条件分岐をする。
3 回答
私の最初の回答
Sub ノック38本目_1() Dim wirteWeekdays_IX As Long: wirteWeekdays_IX = 1 '平日書込み用 Dim writeHolidays_IX As Long: writeHolidays_IX = 1 '休日書込み用 Dim wsEarnings As Worksheet: Set wsEarnings = Worksheets("売上") Dim wswriteHolidays As Worksheet: Set wswriteHolidays = Worksheets("土日祝") Dim wswriteWeekdays As Worksheet: Set wswriteWeekdays = Worksheets("平日") Dim wsPublicHoliday As Worksheet: Set wsPublicHoliday = Worksheets("祝日") Dim endFlg As Boolean For i = 2 To wsEarnings.Range("A1").CurrentRegion.Rows.Count mydate = wsEarnings.Cells(i, 1): endFlg = False '祝日の処理 For j = 1 To wsPublicHoliday.Range("A1").CurrentRegion.Rows.Count If mydate = wsPublicHoliday.Cells(j, 1) Then '祝日書込み処理 If writeHolidays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(1, k) Next End If writeHolidays_IX = writeHolidays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(i, k) Next endFlg = True End If Next '平日、土日の処理 If endFlg = False Then If Weekday(mydate) = 7 Or Weekday(mydate) = 1 Then '休日書込み処理 If writeHolidays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(1, k) Next End If writeHolidays_IX = writeHolidays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(i, k) Next Else '平日書込み処理 If wirteWeekdays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteWeekdays.Cells(wirteWeekdays_IX, k) = wsEarnings.Cells(1, k) Next End If wirteWeekdays_IX = wirteWeekdays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteWeekdays.Cells(wirteWeekdays_IX, k) = wsEarnings.Cells(i, k) Next End If End If Next End Sub
まずは、平日Sheet、土日祝日Sheet書込み用変数を1として宣言します。
また、各Sheetをオブジェクト変数にSetします。
メイン処理は、まず祝日がどうかを変数mydateに売上Sheetの日付を代入し、祝日Sheetのデータをループ処理して探す。
次に、祝日で無い場合は、Weekday関数を使って土日か判断をする。土日であれば、土日祝日Sheet書込みし、それ以外(平日)なら平日Sheetに書込みます。
これをTwitterに投稿したところ、サイト管理者から以下のツッコミがありました。
「祝日の判定をループせずに出来るとより良いと思います。」
ループ処理せずに判定できる方法あるの??と思いました。しかし、サイトの回答を見てみるとCountifを使っていることに気づきました。
そうか、処理対象の日付が祝日Sheetないにあれば、Countifが戻り値が0以上になる!そこで判断すればいいのかということに気づきました。
また、書込み処理部分が長くなってしまうので、書込み処理はサブルーチン化しました。
私の最終回答
'モジュールレベルの変数宣言 Dim wirteWeekdays_IX As Long: Dim writeHolidays_IX As Long Dim wsEarnings As Worksheet: Dim wswriteHolidays As Worksheet Dim wswriteWeekdays As Worksheet: Dim wsPublicHoliday As Worksheet Dim i As Long: Dim endFlg As Boolean: Dim mydate As Date Sub ノック38本目_2() wirteWeekdays_IX = 1 '平日書込み用 writeHolidays_IX = 1 '休日書込み用 Set wsEarnings = Worksheets("売上"): Set wswriteHolidays = Worksheets("土日祝") Set wswriteWeekdays = Worksheets("平日"): Set wsPublicHoliday = Worksheets("祝日") For i = 2 To wsEarnings.Range("A1").CurrentRegion.Rows.Count mydate = wsEarnings.Cells(i, 1): endFlg = False '祝日の処理 If WorksheetFunction.CountIf(wsPublicHoliday.Columns(1), wsEarnings.Cells(i, 1)) > 0 Then '祝日書込み処理 If writeHolidays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(1, k) Next End If writeHolidays_IX = writeHolidays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(i, k) Next endFlg = True End If '平日、土日の処理 If endFlg = False Then Call 書込み処理 End If Next End Sub
サブルーチン、書込み処理
Private Sub 書込み処理() If Weekday(mydate) = 7 Or Weekday(mydate) = 1 Then '休日書込み処理 If writeHolidays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(1, k) Next End If writeHolidays_IX = writeHolidays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteHolidays.Cells(writeHolidays_IX, k) = wsEarnings.Cells(i, k) Next Else '平日書込み処理 If wirteWeekdays_IX = 1 Then For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteWeekdays.Cells(wirteWeekdays_IX, k) = wsEarnings.Cells(1, k) Next End If wirteWeekdays_IX = wirteWeekdays_IX + 1 '書込み変数インクリメント For k = 1 To wsEarnings.Range("A1").CurrentRegion.Columns.Count wswriteWeekdays.Cells(wirteWeekdays_IX, k) = wsEarnings.Cells(i, k) Next End If End Sub
修正点は2点です。
1点目は、祝日の判定処理をCountifに変更しました。
2点目は、休日、平日の書き込み処理をサブルーチン化しました。サブルーチン化すると、処理がサブルーチンのコードに移った時に、プロシージャレベルの変数の値は破棄されるため、書込み用の変数、Sheet用の変数、endFlgはモジュールレベルの変数としました。
ここまでやるなら、祝日の処理も書込み処理のサブルーチンの中に入れれば??と思いましたが、時間がある時に修正したいと思います。
やはり、サブルーチン化するとメイン処理がすっきりと見えます。モジュールレベルの変数をどれにするかが悩ましいところではありますが(;^ω^)。
4 感想
コードの部品化(サブルーチン化)は、可読性をあげる、保守しやすくするために必要だと思っていますが、なかなか手を出せずにいたので今回で少しレベルが上がりました。もっと部品化して、可読性の高いコードを書いていこうと思います。
ではでは、この辺で(^^)/~~~
5 英語の勉強
売上:Earnings
平日:Weekdays
休日:Holiday
祝日:Public Holiday
(土日祝Sheetは「Holidays」と造語を使っております。)