VBA編其ノ7 Do…Loop激闘編 前編
4月から消費税率が8%になりますね(これを書いているのは2014年2月です)。
それに伴い、いろんなデータを書き換えなければならないので、わたしのまわりでもてんやわんやです。
…でね、計算間違うヤツとか出てきて…。
イヤ、もちろん1コ1コ計算とかしないですよ。
だけど、式の設定自体を間違えているとどうしようもないですわな。
リカバーするのも大変です…。
さて、データをアタマからおしりまで計算させる、というのは、手作業でよく発生する作業ですが、今回はコイツをVBAを使って行います。
まず、下↓からサンプルデータのダウンロードを。
開くとこんな↓です※1。
今回は、「税抜価格」のとなりに、「税抜価格×1.08」という列を新設したいと思います。 さて、どうなることやら。
マクロのタイトル
というわけで、新しいマクロを作るのに、まずタイトルを入れようと。 でね、タイトルを「税抜価格×1.08」にしようかと思ったのですが…※2。
怒られてしまいました。
調べてみると、どうやら「×1.08」の「×(かける)」が良くなかったみたいです(実は「 . (小数点、ピリオド)」もアウト)。
マクロには、タイトルに適切でない文字、というのがあるみたいなんですよ。
今回はどうやらそれにひっかかってしまったみたいです。
なので、「8%計算」にタイトルを修正しようかな、と試みたのですが…※3。
こちらも怒られてしまいました。
今度は「 % 」がダメだったみたいです。
結局、タイトルを「税8パーセント」とカタカナにしたらOKでした。
このように、ルール上タイトルとして使用できない文字があったりするので、気を付けましょう。
細かいルールは詳述しませんが、#とか$とか、記号系はだいたいアウト、と覚えておいてください。
列を挿入する
さて、われわれがこれからマクロでやりたいこと、というのは、大きく2つのパートに分かれます。
整理すると、
1. 「税抜価格」列のとなりに新しく列を挿入する
2. 新しく挿入した列に「税抜価格×1.08」の計算結果を入れる
となります。
なので、まず上記1. の部分を作ってからそこに2. をくっつける、という、「ちょっとずつ作る作戦」をしようかな、と。
「…え~?さっさと作ってくれよー」って方もいらっしゃると思いますが、あわてない、あわてない。
一挙に作ると、ミスったときにどこが悪かったかがわかりづらくなるんですよ。なので。
というわけで、列を挿入する、というのをまずやりたいのですが… 列を挿入するには columns(列番号).insert と書きます。 で、「列番号」のところには数字を入れるのです、が、さて問題。 「税抜価格」の右隣りに挿入するには、「列番号」にいくつを指定したらよいでしょうか?
…まあ、すぐにわかりますね。 答えは…、手作業でやるときのことを思い出しましょう※4。 「税抜価格」列の右に列を挿入するとき、その隣の列を選択しますよね。 「税抜価格」は左から3番目なので、それにプラス1した値、すなわち「 4 」を指定します。
というわけで、とりあえず列を挿入するだけのマクロを書いてみました※5 ※6。
別Excelにマクロを使うやり方はもう大丈夫ですか?
マクロの入っているExcelと使う先のExcelを両方とも開いて、使う側で動かす、ですよ。
詳しくは其ノ6、見てくださいね。
さて、※5でわたしは
Columns(3 + 1).Insert
としました。
ココ、別に「 4 」で良いんですが、あえて「 3 + 1 」としています。
なんとなく、わかりやすいかな、と思って。
では、これで1列挿入はOK、 …と行きたいところなんですが、この列、項目名(ヘッダー)がありませんね。 このままではなんかダサイので、「税抜価格×1.08」というタイトルを入れることにします。
Columns(3 + 1).Insert
Cells(1, 3 + 1).Value = "税抜価格×1.08"
Cells(1, 3 + 1).Interir.ColorIndex = 8
単純に、新しく挿入した列の1行目に文字列を入れてやるだけです。
ついでに色も塗ってみました。
なお、さきほど「マクロのタイトルに×とか%とかは使えないよ」という話をしましたが、それはあくまでもマクロのタイトルの話ね。
今回のように、セル内に入れる文字列として""チョンチョンで囲って使う分には問題ありませんので、誤解なきよう。
…では、ここまでの状態で、またマクロを動かしてみましょう。
あ、そうそう、さっき1列挿入した分は消して、元の状態に戻しておいてくださいね。
で、コイツを動かすと…※7 ※8
おおっと!エラーになってしまいました。 なになに?…「オブジェクトは、このプロパティまたはメソッドをサポートしていません。」 …ふーむ、よく意味がわかりませんな。
エラーがでてしまった!
…「オブジェクトは~」の意味はよくわかりませんが、とりあえず、このマクロがエラーになっちゃったのは事実。 なので、「終了」を押して終了させちゃってもよいのですが、今回はためしに「デバッグ」の方を押してみてください。 すると…※9
黄色く表示されたところが、エラーになった箇所です。
「デバッグ」とは「バグを検出し、取り除く」という意味でして、
さきほどのメッセージボックスの「終了」の方を押すとと単純にマクロを強制終了させちゃうんですが、「デバッグ」を押すと、ダメだった箇所を教えてくれるんです。
では、どこが悪かったのでしょうか?
黄色いところをよく読んでみてください。
…まあ、すでにお気づきの方も多いと思いますが…、そうです。
「Interir」になってますね。
「Interior」じゃないといけないのですが。
要するに、スペルミスですな。
というわけで、マクロを修正してやらないといけないわけですが…、その前に、ちょっと画面の方↓を見てください※10。
見てほしいのは一番上のところ。 「中断」ってなってますね。 実は、「デバッグ」中は、問題箇所の一歩手前でマクロを中断してるんです。 ためしに、作業しているワークシートの方を見に行ってみましょう※11。
途中までは動いています。
色が塗られていないだけで。
つまり、色を塗る(ハズの)命令の手前のところで止まってる、というワケです。
でね、さっきのスペルを修正して、この続きから動かすこともできるんですよ。
※10の丸で囲んだ「リセット」のところ、その左に再生ボタンみたいなの、あるじゃないですか。
右向き三角の。
それ押すと、中断箇所からマクロ再開することができます。
つまり、「中断」というのは「一時停止」で、いつでも再開可能なんですね。
ですが、僕が推奨しているのは、「リセット」押して、最初からやり直すというやり方です。
…う~ん、これは僕のポリシーみたいなモンなんですが、道に迷ったときにそこからリカバーしようとすると、余計にワケわからなくなるじゃないですか。
それだったら、面倒でもスタート地点に戻って、最初からやり直した方が、結局は短時間でゴールできるんじゃないかな、と思っておりまして…。
ということで、今回は「リセット」押してください※12。
※10の丸囲みのところにある、正方形のボタンです。
リセットしました。
「中断」の文字が消えましたね。
この状態で、さっきのスペルミスを修正していきましょう※13。
修正できましたら、さっきのマクロのせいで挿入されちゃった列を元に戻して、もう一回マクロを動かしてみてください※14。
「税抜×1.08」を入れる列ができました。
あとは、2行目から計算式が入っていけばOKですよね。
おしりまで計算させる
ここからが今日の本題。 2番目のステップ「新しく挿入した列に「税抜価格×1.08」の計算結果を入れる」の部分を付けたしていってみましょう。 この部分は、其ノ5でやった「Do…Loop」を使って、こんなふう↓にしてみました※15。
Dim i As Integer
Columns(3 + 1).Insert
Cells(1, 3 + 1).Value = "税抜価格×1.08"
Cells(1, 3 + 1).Interior.ColorIndex = 8
i = 2
Do Until Cells(i, 3).Value = ""
Cells(i, 3 + 1).Value = Cells(i, 3).Value * 1.08
i = i + 1
Loop
では、解説。
このマクロは、セルが1コずつ下にずれていきながら計算結果を入れる、という構造になっています。
その、下にずれていく部分を、変数「 i 」で実現したい。
すなわち、「Cells(i, 3 + 1)」というかたちで、行番号に変数「 i 」を使いたい。
なので、まず最初に変数を宣言します。
そして、「 i 」を「 2 」からスタートさせます。
「 1 」ではありません。1行目はヘッダー行だからです。
そして後は、例の「 i = i + 1 」で、次の行に送り、ループさせてやればよい。
で、問題はループ処理の終点ですね。
今回は、「税抜価格」のところが空白になるまでというふうにしています。
なので、「Until Cells(i, 3).Value = ""」と、空の文字列を指定しています。
こうすれば、「税抜価格」さえみっちり埋まっていれば、おしりまで計算結果を入れられますからね。
ということで、解説はこのぐらいにして、動かしてみましょう※16。
あ。端数がでてしまった…。
でもでも、挙動は悪くなさそうですね。
<この機会にちょっとお話>
手作業だと、式を入れた後に値貼りしますよね。
でも、マクロの際は、上↑のようにマクロを書いているかぎり、値貼り付けは必要ありません。
なぜかというと、「value」、すなわち値に直接計算結果を入れているからです。
もし数式として入れたい場合は、
Cells(i, 3 + 1).Formula = "=C" & i & "*1.08"
のように、「formula」の中に数式を""チョンチョンで囲ったかたちで埋め込みます。
ワークシート関数
…すみません、端数対策のことを忘れてました。 端数はですね、丸めてやらないといけないのですが、今回は(今回も)切り捨てというルールでいきましょう。 というわけで、上のマクロにrounddownを仕込もうかと思っているのですが…、 それ以外にも、いくつか改善できるところがあると思うので、さっきのマクロをブラッシュアップしたものを作ってみました※17。
変更点は4つ。
まず①。
withを使ってまとめてみました。
この方が見やすいもんね。
②。 「空白のところまで処理する」のところの列番号を「 1 」にしました。 なぜかというと、もし「税抜価格」に空白があると、そこで処理が止まってしまうから※18です。 価格未確定なのでblancにしておくというケース、あり得ますもんね。
なので、基準列を「 1 」にしました。 1列目は「No」列なので、ほぼ確実におしりまで何かしら値が入っていますからね。
③。
今回のキモです。
実はExcelには「ワークシート関数」と「VBA関数」の2つがあって、「ワークシート関数」というのは、われわれがいつも手作業で使ってる関数のこと。
countifやvlookupなどのことです。
rounddownもそうですね。
ワークシート上で使える関数、くらいに捉えてください。
それに対して「VBA関数」というのは、VBA特有の関数のことです。
でね、VBAから「ワークシート関数」を使うには「Application.WorksheetFunction~」というのを付けてやらないといけないんですよ。
まあ、あの、「これからワークシート関数を使いますよ~」という決まり文句だと思ってください。
なお、VBA関数を使う際にはこのような枕詞は必要なかったりします。
ちょっと不平等な感じもしますが、とりあえず、決まり文句としてApplication.WorksheetFunction、覚えちゃってください。
そこを除けば、関数の使い方はシート上と同じです。
④。マクロ処理が終了したことがわかるように、メッセージが出るようにしました。
というわけで、改良したマクロを動かすと…※19
こんなふうになります。
ということで、計算結果をおしりまで入れるやり方、ガッテンしていただけましたでしょうか。
以下、蛇足なんですが…。
すでに「×1.05」された値(旧税込価格)を「×1.08」の値(新税込価格)に変換する、というのをやろうかと。
税抜価格をデータとして持っていない、というケースもありますからね。
そういう「×1.05」された値しか持っていないときに、新税込価格を割り出す、というのをやりたいと思います。
…で、なぜコレをやろうかと思ったかというと…、あの…、動機は個人的なリベンジでして。
冒頭でボヤいたように、仲間がコレの変換にミスりまして。
それで、この機会にやっておこうかと。
ということで、プロシージャです。 上↑のマクロで増えた列は元に戻しておいてくださいね。 「税込価格×1.05」がD列にあることを想定してプロシージャ書いてるので。
Dim i As Integer
Columns(4 + 1).Insert
With Cells(1, 4 + 1)
.Value = "5%→8%に"
.Interior.ColorIndex = 6
End With
i = 2
Do Until Cells(i, 1).Value = ""
Cells(i, 4 + 1).Value = Application.WorksheetFunction.RoundDown(Application.WorksheetFunction.RoundUp(Cells(i, 4).Value / 1.05, 0) * 1.08, 0)
i = i + 1
Loop
MsgBox "計算終了"
…そう。
要するに、一度1.05で割って「税抜価格」を割り出して、それに1.08を掛ける、ということです。
動かした結果はこうなります※20。
…まあ、やってることは単純なんだけど、1コだけ注意点。
今やってもらってるリストでは、「税込計算で出た端数は切り捨て」というルールで動いているんですよ。
だから、「×1.05」の値を税抜の状態に戻すとき、逆にroundupしてやらないといけない※21。
なぜなら、かつて旧税込価格を出したときにrounddownをしてたから、なんです。
きちんと逆の手順をとらないと、元の状態には戻せませんからね。
(ちなみに、僕の仲間はこの「一回roundupする」ところを理解していませんでした。だから、最終的に1円違って大あわて。)
何だかループ処理の話題からどんどんポイントがずれてきちゃいましたが、何かの参考に。
あと、上のプロシージャ読んでもらえばわかると思いますが、「Application.WorksheetFunction~」はその都度書かなきゃいけない(今回のケースで言うと、2回書かなきゃいけない)みたいなんで、そのことも覚えておいてください。