[VBA実践]とにかくマクロを完成させよう![#2]

記事
IT・テクノロジー
前回の記事はこちら

さて、前回までで、
1行分のデータを抽出対象か、そうでないか判定するところまでは書きました。
以下のようなコードになっているはずです。
Sub 担当者ごと抽出()
    Dim targetName As String
    targetName = Cells(1, 9).Value
    If Cells(2, 6).Value = targetName Then
        '一緒だった時の処理
        Debug.Print "担当者名が" & targetName & "と同じです!"
    Else
        'そうでない時の処理
        Debug.Print "担当者名が" & targetName & "ではありません!"
    End If
End Sub

この状態から、「まずは完成させること」を最優先して、実際にコードを書いてみましょう。


1行分の処理を完成させる

さて、前回まででは「抽出担当者名と等しいかを判定するところまで」で終わっていました。
あと必要なのは、
1.等しい場合、A列~F列の値をK列からP列にそれぞれ転記する
2.1行に対して行った処理を、入力されているすべての行に対して繰り返す
という処理ですね!
1行分.png

1行分転記先.png
さて、今Excelのシート上では、上の画像のような配置になっていると思います。
担当者名が抽出対象と同じであれば、A列の値をK列の値に代入すれば良いことになりますね。
A2セルの値をK2セルに代入するコードはすぐに思いつきますか?

Cells(2,11).Value = Cells(2,1).Value
もしくは
Range("K2").Value = Range("A2").Value
ですね!
ここではCellsを使っておきましょう。Rangeだと後々面倒なことになります。

これを「抽出担当者名と等しい場合」通る部分に書けばいいので、
Sub 担当者ごと抽出()
    Dim targetName As String
    targetName = Cells(1, 9).Value
    If Cells(2, 6).Value = targetName Then
        '一緒だった時の処理
        Debug.Print "担当者名が" & targetName & "と同じです!"
Cells(2,11).Value = Cells(2,1).Value
    Else
        'そうでない時の処理
        Debug.Print "担当者名が" & targetName & "ではありません!"
    End If
End Sub

これで試しに抽出担当者名を1行目の担当者名と同じにして実行すると…
番号のみ.png
ちゃんとNoが入力されていますね!
あとは、他の列の値も同様に代入しておくと

Sub 担当者ごと抽出()
    Dim targetName As String
    targetName = Cells(1, 9).Value
    If Cells(2, 6).Value = targetName Then
        '一緒だった時の処理
        Debug.Print "担当者名が" & targetName & "と同じです!"
        Cells(2,11).Value = Cells(2,1).Value
        Cells(2,12).Value = Cells(2,2).Value
        Cells(2,13).Value = Cells(2,3).Value
        Cells(2,14).Value = Cells(2,4).Value
        Cells(2,15).Value = Cells(2,5).Value
        Cells(2,16).Value = Cells(2,6).Value
    Else
        'そうでない時の処理
        Debug.Print "担当者名が" & targetName & "ではありません!"
    End If
End Sub

これでA列~F列の内容がK列~P列に転記されればOKですね!
1行分転記完了.png
上の画像のように、正しく転記されていれば大丈夫です!
もし転記されていない場合は、セルの指定がずれていないかなど確認してみて下さいね。(おそらく判定まで正しく出来ているなら、そこ以外にミスるポイントは無いはずなので)
これで1行分は完成です!


あとは繰り返すだけ!

ここまで来たら、あとは入力されている行分繰り返すだけです。

「繰り返し」は以下のように表現しましたね。
For 変数=最初の値 To 最後の値
    ここに繰り返したい処理を書く
Next

今回は行ごとに繰り返したいので、A列の値をK列に代入する部分を例に出すと
Cells(2,11).Value = Cells(2,1).Value
Cells(rowLong,11).Value = Cells(rowLong,1).Value
という風にすればいいですね!(rowLongを変数として、最初の行数から最後の行数までの数値を代入していきます)

では、繰り返したいのはどこからどこまでしょうか?
自分で書いたコードを見て、一度考えて下さいね。

間違い.png

「繰り返したいのは値を代入するところだから、この赤枠の部分だ!」と思った方もいそうですね。
もし上記の赤枠内のみを繰り返してしまうとどうなるでしょうか?
間違い1.png
実際に書くと、上の画像のようになります。
rowLongというLong型の変数の宣言を追加し、その変数にFor文で2~16の値が入るようにしています。(現在2行目から16行目まで値が入っているため)
これを実行すると…
間違い結果.png
…こんな風になってしまいます。
もちろん抽出担当者名は「山田」と入力しているので、想定では「山田」以外の人は入らないはずです。

なぜこうなるか、分かりますか?
それは、「抽出担当者名と担当者名が等しいかどうか」を判定する処理を、最初のデータに対してのみ行っているからですね。

正しくは、「1行ごとに抽出担当者と担当者名が等しいかどうか判定する」必要があるので、
正解.png
上の画像の赤枠部分を繰り返す必要があります。
というわけで、以下のようなコードが正解となります。

Sub 担当者ごと抽出()
    Dim targetName As String
    Dim rowLong As Long
    targetName = Cells(1, 9).Value
For rowLong = 2 To 16
        If Cells(rowLong, 6).Value = targetName Then
            '一緒だった時の処理
            Debug.Print "担当者名が" & targetName & "と同じです!"
            Cells(rowLong, 11).Value = Cells(rowLong, 1).Value
            Cells(rowLong, 12).Value = Cells(rowLong, 2).Value
            Cells(rowLong, 13).Value = Cells(rowLong, 3).Value
            Cells(rowLong, 14).Value = Cells(rowLong, 4).Value
            Cells(rowLong, 15).Value = Cells(rowLong, 5).Value
            Cells(rowLong, 16).Value = Cells(rowLong, 6).Value
        Else
            'そうでない時の処理
            Debug.Print "担当者名が" & targetName & "ではありません!"
        End If
  Next
End Sub

注意すべき点は、
If Cells(rowLong, 6).Value = targetName Then
この部分の行数をrowLongに変えておくことですね。

先程実行した結果を削除して、修正後のマクロを実行してみると…
正解か.png
…う~ん?なんか、思ってたのと違う気がしますよね。
確かに先程までと違い、指定した担当者のデータのみ転記されていますが、なぜか行が飛び飛びです。

実は、A~F列の値をK~P列に代入する際、
Cells(rowLong, 11).Value = Cells(rowLong, 1).Value
という風に、同じ変数で行を指定してしまっていたのが原因なんですよね。

余計な空白行はもちろん無い方が良いので、この書き方ではまずいわけです。
というわけで、まず転記先の行数はexRowLongという別の変数を使うことにしましょう。
Sub 担当者ごと抽出()
    Dim targetName As String
    Dim rowLong As Long
    Dim exRowLong As Long
    targetName = Cells(1, 9).Value
exRowLong = 2
    For rowLong = 2 To 16
        If Cells(rowLong, 6).Value = targetName Then
            '一緒だった時の処理
            Debug.Print "担当者名が" & targetName & "と同じです!"
            Cells(exRowLong, 11).Value = Cells(rowLong, 1).Value
            Cells(exRowLong, 12).Value = Cells(rowLong, 2).Value
            Cells(exRowLong, 13).Value = Cells(rowLong, 3).Value
            Cells(exRowLong, 14).Value = Cells(rowLong, 4).Value
            Cells(exRowLong, 15).Value = Cells(rowLong, 5).Value
            Cells(exRowLong, 16).Value = Cells(rowLong, 6).Value
        Else
            'そうでない時の処理
            Debug.Print "担当者名が" & targetName & "ではありません!"
        End If
    Next
End Sub

さて、これで実行してみると…
失敗.png
今度はなぜか1つしか転記されず、しかも抽出担当者と担当者名が等しい最後のデータが入っていました。

何が原因か、分かりますか?
はい、rowLongはFor文で繰り返し通るたびに1ずつ勝手に増えてくれるようになっていますが、exRowLongという変数はFor文とは無関係な変数なので、何度通ろうが、勝手に変化するようなことはありません。

ではどうするかと言えば、必要な時に1増えるように書けば良いんですね。
exRowLongの値を1増やすのであれば、
exRowLong = exRowLong + 1
と書けば、元の数値よりも1大きい数値がexRowLongに代入されます。

これをどのタイミングで実行するかが大切ですね。
例えば2行目のセルに転記が行われたら、次は3行目のセルに転記したい、ということになるので、転記処理を行った直後に行数を増やせば良さそうですね。

Sub 担当者ごと抽出()
    Dim targetName As String
    Dim rowLong As Long
    Dim exRowLong As Long
    targetName = Cells(1, 9).Value
    exRowLong = 2
    For rowLong = 2 To 16
        If Cells(rowLong, 6).Value = targetName Then
            '一緒だった時の処理
            Debug.Print "担当者名が" & targetName & "と同じです!"
            Cells(exRowLong, 11).Value = Cells(rowLong, 1).Value
            Cells(exRowLong, 12).Value = Cells(rowLong, 2).Value
            Cells(exRowLong, 13).Value = Cells(rowLong, 3).Value
            Cells(exRowLong, 14).Value = Cells(rowLong, 4).Value
            Cells(exRowLong, 15).Value = Cells(rowLong, 5).Value
            Cells(exRowLong, 16).Value = Cells(rowLong, 6).Value
exRowLong = exRowLong + 1
        Else
            'そうでない時の処理
            Debug.Print "担当者名が" & targetName & "ではありません!"
        End If
    Next
End Sub

これで良さそうですね!
実行してみると…
成功.png
はい、ようやく思い通りの結果になりました!


終わりに

はい、少し長くなりましたが、予定通り、最初に想定していた処理を実行するマクロは完成させることが出来ました。

完成したのはもちろん大切ですが、今回紹介したように、
「思った通りの結果にならない時に、何がまずかったかを考えること」
「考えた結果、『ここがおかしいのかな』と仮説を立て、修正し、再度試してみること」
こそ、今回の記事で最も重要だと思っています。というか、プログラミングにおいて、これが何よりも大事なことだと思っています。

VBAを書き始めてすぐであれば、おそらく今回の記事以上に、ミスやエラーの連発になります。
そうなっても諦めず、焦らず、一つひとつの処理が正しく実行されているかをF8キーでステップイン実行してみたり、Debug.Printで変数の中身を確認したり、判定が想定通りになっているか確認したりしながら修正していくうちに、少しずつ完成に近付いていきます。

ノーミスで完成させる必要は全く無いので、「10回間違えても10回修正すれば良いや」くらいの気持ちで取り組んで下さい。

さて、一応完成させた今回のマクロですが、まだまだブラッシュアップすべき部分があります。
・元となるデータの行数が増えた場合に対応出来ない(例えば今回のマクロのまま、100行のデータなどには対応不可)
・転記する際、列を一つひとつ指定していて面倒
・そもそも同じシート内ではなく、別のシートに転記したい

次回からは、上記のような点を踏まえ、今回作成したマクロをより汎用性の高い、使いやすいマクロにしていきます。

今回解説した内容で、もし理解しづらい点があったり、「こういうやり方でも問題ないか」という質問がある方は、以下の有料サポートをご利用下さい。
出来るだけ迅速に、丁寧に対応致します。
今回も最後までお疲れ様でした!

次の記事はこちら。

サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す