質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

2回答

698閲覧

VBAで別シートの内容を参照して、欠落している時刻があれば行追加したい

qwe001

総合スコア133

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

0クリップ

投稿2022/08/24 10:02

編集2022/08/24 10:08

サンプル

https://docs.google.com/spreadsheets/d/e/2PACX-1vSXvCjZB5eIS_ojLOxKiCsf7rfH4qDWfXDMko6KnkWVRfAn9CIMN3C2DEipChB1g2VGpLiqLdY6vmTT/pubhtml

前提

  • イベントリストシート(EventListsシート)には、曜日別、開始時刻別のイベント一覧が記載されています。
  • マスターシート(Masterシート)には、日付別のイベント一覧が記載されています。
  • マスターシート(Masterシート)に入るデータは、一か月分のみです
  • 開始曜日は月曜日とします
  • 曜日の区切りは 05:00 ~ 28:59 とします

やりたいこと

Masterシートについて、EventListsシートに存在しない時刻があれば、行追加して、対象時刻を埋め込みたいです
サンプルURLにある、期待結果シートのような形にしたいです。(背景色はつかなくていいです)

現状、Masterシートは以下のような並びですが

イベント日曜日開始時刻種別
20220901502B
20220901502B
20220901502A
20220901502A
20220901502A
20220901502A
20220901510A
20220901510A
20220901606A
20220901606A

EventListsシートのC列 開始時間を見ると、5:00からのイベントがありますので、
以下のように、先頭に行追加したいです

種別も見分けがつきやすいように、ADDとつけたいです

イベント日曜日開始時刻種別
20220901500ADD
20220901502B
20220901502B
20220901502A
20220901502A
20220901502A
20220901502A
20220901510A
20220901510A
20220901606A
20220901606A

現在の実装

vba

1Sub appendPositionToMaster() 2 Dim ws As Worksheet 3 Dim ws2 As Worksheet 4 Dim lastRowNum As Long 5 Dim lastRowNum2 As Long 6 Dim i As Integer 7 Dim j As Integer 8 Dim weekName As String 9 Dim startTime 10 Dim weekName2 As String 11 Dim startTime2 12 13 Set ws = Worksheets("EventLists") 14 Set ws2 = Worksheets("Master") 15 16 ' A列の最終行番号を取得 17 lastRowNum = ws.Range("A" & Rows.Count).End(xlUp).row 18 lastRowNum2 = ws2.Range("A" & Rows.Count).End(xlUp).row 19 20 ' EventListsシート 行単位でループ 21 For i = 2 To lastRowNum 22 ws.Activate 23 24 ' L列(fix_week_name) の値を取得 25 weekName = Cells(i, 12).Value 26 ' M列(fix_start_time) の値を取得 27 startTime = Cells(i, 13).Value 28 29 ' M列(fix_start_time) の値をセミコロンなしの数値に変換する(0.208333333 -> "5:00" -> 500) 30 startTime = Val(Format(startTime, "hmm")) 31 32 ' Masterシート 行単位でループ 33 For j = 2 To lastRowNum2 34 ws2.Activate 35 36 ' B列(曜日) の値を取得 37 weekName2 = Cells(j, 2).Value 38 ' C列(開始時刻) の値を取得 39 startTime2 = Cells(j, 3).Value 40 41 If weekName2 = "" Then Exit For 42 43 ' C列(開始時刻) の値を数値に変換する("502" -> 502) 44 startTime2 = Val(startTime2) 45 46 ' 曜日が同じの時 47 ' EventListsの開始時刻がMasterシートの開始時刻よりも早い時 48 If weekName = weekName2 And startTime > startTime2 Then 49 MsgBox "Hit" 50 Rows(j).Insert 51 Exit For 52 End If 53 Next j 54 Next i 55End Sub

困っていること

  • VBAで、行追加して、任意の値を代入する方法がわからないです
  • ループ処理の適切な条件設定がわからないです

どのようにすれば、やりたいことが達成できますでしょうか。
VBAは経験が少なくてよくわからないです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hatena19

2022/08/24 14:52 編集

Masterシートは、日付、開始時刻の昇順になってますか。 また、エクセルのバージョンはなんでしょうか。
qwe001

2022/08/25 01:22

日付、開始時刻の昇順です。ExcelのバージョンはMicrosoft® Excel® 2019 MSO (バージョン 2111 ビルド 16.0.14701.20204) 32 ビット です。よろしくお願いいたします。
tatsu99

2022/08/27 01:02 編集

Masterに行を追加するのではなく、別のシートにMatserの行とEventListsの必要な行を追記する方法でも良いですか。最終的にMasterのシートが欲しいなら、Materシートを削除して、別のシートをMasterにRenameすればよいかと思います。 別のシートに出力するのはMatserのA列~H列までです。 EventListsの出力項目も、MatserのA列~H列に該当する列になります。 EventListsのP列を作業用に使用します。 (M列時刻(5:00)を数値(500)に変換したものをP列に書き込みます) 上記で良ければ、対応可能です。
qwe001

2022/08/28 01:23

@tatsu99 ご連絡ありがとうございます。それで大丈夫です。Masterシート自体、私が他社から提供されたオリジナルのシートを作業しやすいように型変換などをしたシートですので。(もともとのシートは全ての値が文字列形式になっていて値の比較ができませんでした) よろしくお願いいたします。
guest

回答2

0

ベストアンサー

空のシート:"出力" を作成してから実行してください。
そのほかは「質問への追記・修正の依頼」で記述した通りです。
不明点があれば、補足してください。

VBA

1Option Explicit 2Public Sub イベント追加() 3 Dim ws As Worksheet 'Event Lists 4 Dim ws2 As Worksheet 'Master 5 Dim ws3 As Worksheet '出力 6 Dim lastRowNum As Long 'Event Lists最終行 7 Dim lastRowNum2 As Long 'Master 最終行 8 Dim weekName As String '曜日 9 Dim PrevName As String '前回曜日 10 11 Dim startTime As Variant '開始時刻(h:mm)(Event Lists) 12 Dim startTimeL As Long '開始時刻(hmm)(Event Lists) 13 Dim startTimeL2 As Long '開始時刻(hmm)(Master) 14 Dim MasterDate As Long '日付(Master) 15 Dim PrevDate As Long '前回日付(Master) 16 Dim weekName2 As String 17 Dim key As Variant 18 Dim wrow As Long '行番号(EventLists) 19 Dim wrow2 As Long '行番号(Master) 20 Dim wrow3 As Long '行番号(出力) 21 Dim strow_dic As Object '開始行番号(曜日単位) 22 Dim enrow_dic As Object '終了行番号(曜日単位) 23 Dim st_row As Long '開始行番号(EventLists) 24 Dim en_row As Long '終了行番号(EventLists) 25 Dim cur_row As Long '処理中行番号(EventLists) 26 Set strow_dic = CreateObject("Scripting.Dictionary") 27 Set enrow_dic = CreateObject("Scripting.Dictionary") 28 Set ws = Worksheets("EventLists") 29 Set ws2 = Worksheets("Master") 30 Set ws3 = Worksheets("出力") 31 ' A列の最終行番号を取得 32 lastRowNum = ws.Range("A" & Rows.Count).End(xlUp).Row 33 lastRowNum2 = ws2.Range("A" & Rows.Count).End(xlUp).Row 34 ' EventListsシート 行単位でループ 35 PrevName = "" 36 For wrow = 2 To lastRowNum 37 ' L列(fix_week_name) の値を取得 38 weekName = ws.Cells(wrow, "L").Value 39 ' M列(fix_start_time) の値を取得 40 startTime = ws.Cells(wrow, "M").Value 41 ' M列(fix_start_time) の値をセミコロンなしの数値に変換する(0.208333333 -> "5:00" -> 500) 42 If startTime < 1 Then 43 startTimeL = Val(Format(startTime, "hmm")) 44 Else 45 startTimeL = Val(Format(startTime - 1, "hmm")) + 2400 46 End If 47 '曜日変わり時に開始行番号を設定 48 If weekName <> PrevName Then 49 strow_dic(weekName) = wrow 50 End If 51 '終了行番号を設定 52 enrow_dic(weekName) = wrow 53 '開始時刻(数値)を設定 54 ws.Cells(wrow, "P").Value = startTimeL 55 '前回曜日を保存 56 PrevName = weekName 57 Next 58 '出力シートクリア&見出し設定 59 ws3.Cells.ClearContents 60 wrow3 = 1 61 wrow2 = 1 62 Call put_master(ws2, wrow2, ws3, wrow3) 'Matser1行出力 63 ' Masterシート 行単位でループ 64 PrevDate = 0 65 en_row = -1 66 cur_row = 0 67 For wrow2 = 2 To lastRowNum2 68 ' A列の日付を取得 69 MasterDate = ws2.Cells(wrow2, "A").Value 70 ' B列(曜日) の値を取得 71 weekName2 = ws2.Cells(wrow2, "B").Value 72 ' C列(開始時刻) の値を取得 73 startTimeL2 = ws2.Cells(wrow2, "C").Value 74 '日付変更時の処理 75 If PrevDate <> MasterDate Then 76 '残っているEvent行を出力する 77 Call flush_event(PrevDate, ws, cur_row, en_row, ws3, wrow3) 78 st_row = strow_dic(weekName2) 79 en_row = enrow_dic(weekName2) 80 cur_row = st_row 81 End If 82 'EventListsの当日分が未処理ならEventListsを処理 83 If cur_row <= en_row Then 84 startTimeL = ws.Cells(cur_row, "P").Value 85 'Masterの時刻>EventListsの時刻であるEventListsを全て出力 86 If startTimeL2 >= startTimeL Then 87 Call put_past_event(MasterDate, startTimeL2, ws, cur_row, en_row, ws3, wrow3) 88 End If 89 End If 90 'Matser1行出力 91 Call put_master(ws2, wrow2, ws3, wrow3) 92 '前回日付を保存 93 PrevDate = MasterDate 94 Next 95 '残っているEvent行を出力する 96 Call flush_event(PrevDate, ws, cur_row, en_row, ws3, wrow3) 97 MsgBox ("完了") 98End Sub 99'Master1行出力 100Private Sub put_master(ws2 As Worksheet, wrow2 As Long, ws3 As Worksheet, wrow3) 101 ws3.Range("A" & wrow3).Resize(, 8).Value = ws2.Range("A" & wrow2).Resize(, 8).Value 'A~H 102 wrow3 = wrow3 + 1 103End Sub 104'残Event Lists 出力 105Private Sub flush_event(ByVal msdate As Long, ws As Worksheet, cur_row As Long, en_row As Long, ws3 As Worksheet, wrow3 As Long) 106 Dim wrow As Long 107 For wrow = cur_row To en_row 108 Call put_event(msdate, ws, wrow, ws3, wrow3) '1行出力 109 Next 110End Sub 111'Master時刻より小さい時刻のEvent Listsを出力(MasterとEventListsの時刻が同じ場合は出力しない) 112Private Sub put_past_event(ByVal msdate As Long, ByVal mstime As Long, ws As Worksheet, cur_row As Long, en_row As Long, ws3 As Worksheet, wrow3 As Long) 113 Dim st_time As Long 114 Do 115 If cur_row > en_row Then Exit Do 116 st_time = ws.Cells(cur_row, "P").Value 117 If mstime < st_time Then Exit Do 118 If mstime > st_time Then 119 Call put_event(msdate, ws, cur_row, ws3, wrow3) '1行出力 120 End If 121 cur_row = cur_row + 1 122 Loop 123End Sub 124 125'Event Lists1行出力 126Private Sub put_event(ByVal msdate As Long, ws As Worksheet, ByVal wrow As Long, ws3 As Worksheet, wrow3 As Long) 127 ws3.Cells(wrow3, "A").Value = msdate '日付 128 ws3.Cells(wrow3, "B").Value = ws.Cells(wrow, "L").Value '曜日 129 ws3.Cells(wrow3, "C").Value = ws.Cells(wrow, "P").Value '時刻 130 ws3.Cells(wrow3, "D").Value = "ADD" '種別 131 ws3.Range("E" & wrow3).Resize(, 4).Value = ws.Range("F" & wrow).Resize(, 4).Value 'culumn1-4 132 wrow3 = wrow3 + 1 133End Sub 134

投稿2022/08/28 01:38

編集2022/08/28 04:00
tatsu99

総合スコア5438

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

qwe001

2022/08/28 02:03

ありがとうございます!!まだ精査できてませんが、望んでいる形をほぼ100%実現できているように思えます。曜日名が0になっている(EventListsシートのP列を上書きしているためと思われる)などの課題がありますが、このあたりは私でも調整できそうです。引き続きデータ検証して、問題なさそうならBAをつけさせて頂きます。
tatsu99

2022/08/28 03:15 編集

>曜日名が0になっている(EventListsシートのP列を上書きしているためと思われる) シート:出力の曜日名が0になっているということでしょうか。 こちらでは、そのような現象は発生していません。 参考までに、こちらで使用したEventListsとMasterです。 Masterはテストのため、いろいろ変えています。 https://firestorage.jp/download/efb8516e571703acefa822702ff90384642df73d
tatsu99

2022/08/28 03:58

すみません。 一部訂正します。 'Event Lists1行出力 Private Sub put_event(ByVal msdate As Long, ws As Worksheet, ByVal wrow As Long, ws3 As Worksheet, wrow3 As Long) ws3.Range("A" & wrow3).Resize(, 2).Value = ws.Range("A" & wrow).Resize(, 2).Value '日付と曜日 ws3.Cells(wrow3, "A").Value = msdate '日付 ws3.Cells(wrow3, "B").Value = ws.Cells(wrow, "L").Value '曜日 ws3.Cells(wrow3, "C").Value = ws.Cells(wrow, "P").Value '時刻 ws3.Cells(wrow3, "D").Value = "ADD" '種別 ws3.Range("E" & wrow3).Resize(, 4).Value = ws.Range("F" & wrow).Resize(, 4).Value 'culumn1-4 wrow3 = wrow3 + 1 End Sub の ws3.Range("A" & wrow3).Resize(, 2).Value = ws.Range("A" & wrow).Resize(, 2).Value '日付と曜日 ws3.Cells(wrow3, "A").Value = msdate '日付 は不要です。 この行を削除してください。 回答のほうも修正しておきます。
qwe001

2022/08/28 04:32

正しく、私が求めていた通りの出力が得られました。本当にありがとうございました。一点、訂正がありまして。 '開始時刻(数値)を設定 ws.Cells(wrow, "P").Value = startTimeL こちらは ws.Cells(wrow, "Q").Value = startTimeL にします。 同様に、他にP列を参照しているものも Q列に変更します。 P列にある値は、現在セルが上のセルと同じ曜日かどうかを判定してインデックス番号を生成し、L列の曜日名を生成するのに使用します。 現在のVBAコードのままですと、出力シートに追加した行の、B列 曜日名が0になるので、それを調整すれば、期待動作になりました。 明日までに納品しなければいけなかったので、間に合って本当に助かりました…!!
tatsu99

2022/08/28 04:39

P列は使用されている列だったのですね。空いている列と勘違いしました。失礼しました。 期待した結果になってなによりです。
guest

0

サンプルコードを書きかけたのですが、結構、複雑なコードになりそうです。
とりあえず、ヒントだけ出しておきます。


.Activate でシートのアクティブを切り替えるのはやめましょう。処理が遅くなるだけです。
ws、ws2 とシートを変数に代入してますので、それを使えばアクティブでないシートでも処理できます。
下記のような感じです。

vba

1 ' L列(fix_week_name) の値を取得 2 weekName = ws.Cells(i, 12).Value

行挿入する場合は、挿入した分だけそれ以降の行がずれていきます。最終行もずれます。
ですので、For Next ではなく、Do Loop を使ってセルが空白になるまで繰り返すという処理にした方がいいでしょう。あるいは、For Next を使うなら、最終行から前へ移動しながら処理をするというようにします。


行挿入して、その行に値を代入する方法は、下記のような感じで。

vba

1 ws2.Rows(j).Insert 2 ws2.Cells(j, "B").Value = weekName 3 ws2.Cells(j, "C").Value = startTime 4 ws2.Cells(j, "D").Value = "ADD"

投稿2022/08/25 07:18

hatena19

総合スコア33715

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

qwe001

2022/08/28 01:33

ヒントを出して頂きありがとうございます。引き続き私も試行錯誤していますが、思ってたより複雑で難航しています。。 `ws.Activate` については、動作OKになったら `Application.ScreenUpdating = false/true` をコードの上下につけて処理速度低下を防ぐ予定でした。でも、Withステートメントを使えばシートをアクティブにしなくてもセル操作できるんですね。以後、VBA開発するときはそれを使おうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問