---topics:ループ処理、ワークシート関数---

VBA編其ノ7 Do…Loop激闘編 前編

4月から消費税率が8%になりますね(これを書いているのは2014年2月です)。 それに伴い、いろんなデータを書き換えなければならないので、わたしのまわりでもてんやわんやです。 …でね、計算間違うヤツとか出てきて…。 イヤ、もちろん1コ1コ計算とかしないですよ。 だけど、式の設定自体を間違えているとどうしようもないですわな。 リカバーするのも大変です…。
さて、データをアタマからおしりまで計算させる、というのは、手作業でよく発生する作業ですが、今回はコイツをVBAを使って行います。 まず、下↓からサンプルデータのダウンロードを。

開くとこんな↓です※1

サンプルデータ
※1 サンプルデータ

今回は、「税抜価格」のとなりに、「税抜価格×1.08」という列を新設したいと思います。 さて、どうなることやら。

マクロのタイトル

というわけで、新しいマクロを作るのに、まずタイトルを入れようと。 でね、タイトルを「税抜価格×1.08」にしようかと思ったのですが…※2

「×(かける)」はタイトルに使用できません
※2 「×(かける)」はタイトルに使用できません

怒られてしまいました。
調べてみると、どうやら「×1.08」の「×(かける)」が良くなかったみたいです(実は「 . (小数点、ピリオド)」もアウト)。 マクロには、タイトルに適切でない文字、というのがあるみたいなんですよ。 今回はどうやらそれにひっかかってしまったみたいです。
なので、「8%計算」にタイトルを修正しようかな、と試みたのですが…※3

「 % 」はタイトルに使用できません
※3 「 % 」はタイトルに使用できません

こちらも怒られてしまいました。 今度は「 % 」がダメだったみたいです。
結局、タイトルを「税8パーセント」とカタカナにしたらOKでした。 このように、ルール上タイトルとして使用できない文字があったりするので、気を付けましょう。 細かいルールは詳述しませんが、#とか$とか、記号系はだいたいアウト、と覚えておいてください。

列を挿入する

さて、われわれがこれからマクロでやりたいこと、というのは、大きく2つのパートに分かれます。 整理すると、
1. 「税抜価格」列のとなりに新しく列を挿入する
2. 新しく挿入した列に「税抜価格×1.08」の計算結果を入れる
となります。
なので、まず上記1. の部分を作ってからそこに2. をくっつける、という、「ちょっとずつ作る作戦」をしようかな、と。 「…え~?さっさと作ってくれよー」って方もいらっしゃると思いますが、あわてない、あわてない。 一挙に作ると、ミスったときにどこが悪かったかがわかりづらくなるんですよ。なので。

というわけで、列を挿入する、というのをまずやりたいのですが… 列を挿入するには columns(列番号).insert と書きます。 で、「列番号」のところには数字を入れるのです、が、さて問題。 「税抜価格」の右隣りに挿入するには、「列番号」にいくつを指定したらよいでしょうか?

…まあ、すぐにわかりますね。 答えは…、手作業でやるときのことを思い出しましょう※4。 「税抜価格」列の右に列を挿入するとき、その隣の列を選択しますよね。 「税抜価格」は左から3番目なので、それにプラス1した値、すなわち「 4 」を指定します。

「税抜価格」の列番号は「 3 」 その隣なので、列番号は「 3+1 」
※4 「税抜価格」の列番号は「 3 」 その隣なので、列番号は「 3+1 」

というわけで、とりあえず列を挿入するだけのマクロを書いてみました※5 ※6

「税抜価格」列の隣に1列挿入するマクロ
※5 「税抜価格」列の隣に1列挿入するマクロ
動かすとこんな感じ
※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

上のマクロを正直に動かすと…
※7 上のマクロを正直に動かすと…
怒られてしまった
※8 怒られてしまった

おおっと!エラーになってしまいました。 なになに?…「オブジェクトは、このプロパティまたはメソッドをサポートしていません。」 …ふーむ、よく意味がわかりませんな。

エラーがでてしまった!

…「オブジェクトは~」の意味はよくわかりませんが、とりあえず、このマクロがエラーになっちゃったのは事実。 なので、「終了」を押して終了させちゃってもよいのですが、今回はためしに「デバッグ」の方を押してみてください。 すると…※9

問題箇所がわかる
※9 問題箇所がわかる

黄色く表示されたところが、エラーになった箇所です。
「デバッグ」とは「バグを検出し、取り除く」という意味でして、 さきほどのメッセージボックスの「終了」の方を押すとと単純にマクロを強制終了させちゃうんですが、「デバッグ」を押すと、ダメだった箇所を教えてくれるんです。
では、どこが悪かったのでしょうか? 黄色いところをよく読んでみてください。 …まあ、すでにお気づきの方も多いと思いますが…、そうです。 「Interir」になってますね。 「Interior」じゃないといけないのですが。 要するに、スペルミスですな。
というわけで、マクロを修正してやらないといけないわけですが…、その前に、ちょっと画面の方↓を見てください※10

「デバッグ」はマクロ中断中になる
※10 「デバッグ」はマクロ中断中になる

見てほしいのは一番上のところ。 「中断」ってなってますね。 実は、「デバッグ」中は、問題箇所の一歩手前でマクロを中断してるんです。 ためしに、作業しているワークシートの方を見に行ってみましょう※11

列挿入、項目名入力まではOK

※11 列挿入、項目名入力まではOK

途中までは動いています。 色が塗られていないだけで。
つまり、色を塗る(ハズの)命令の手前のところで止まってる、というワケです。
でね、さっきのスペルを修正して、この続きから動かすこともできるんですよ。 ※10の丸で囲んだ「リセット」のところ、その左に再生ボタンみたいなの、あるじゃないですか。 右向き三角の。 それ押すと、中断箇所からマクロ再開することができます。 つまり、「中断」というのは「一時停止」で、いつでも再開可能なんですね。
ですが、僕が推奨しているのは、「リセット」押して、最初からやり直すというやり方です。 …う~ん、これは僕のポリシーみたいなモンなんですが、道に迷ったときにそこからリカバーしようとすると、余計にワケわからなくなるじゃないですか。 それだったら、面倒でもスタート地点に戻って、最初からやり直した方が、結局は短時間でゴールできるんじゃないかな、と思っておりまして…。
ということで、今回は「リセット」押してください※12。 ※10の丸囲みのところにある、正方形のボタンです。

一回リセット
※12 一回リセット

リセットしました。
「中断」の文字が消えましたね。 この状態で、さっきのスペルミスを修正していきましょう※13

修正できましたら、さっきのマクロのせいで挿入されちゃった列を元に戻して、もう一回マクロを動かしてみてください※14

「interior」に直した
※13 「interior」に直した
こうなって欲しかったのだ
※14 こうなって欲しかったのだ

「税抜×1.08」を入れる列ができました。
あとは、行目から計算式が入っていけば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

3列目が空白になるまでループさせる
※15 3列目が空白になるまでループさせる

では、解説。
このマクロは、セルが1コずつ下にずれていきながら計算結果を入れる、という構造になっています。 その、下にずれていく部分を、変数「 i 」で実現したい。 すなわち、「Cells(i, 3 + 1)」というかたちで、行番号に変数「 i 」を使いたい。 なので、まず最初に変数を宣言します。 そして、「 i 」を「 2 」からスタートさせます。 「 1 」ではありません。1行目はヘッダー行だからです。 そして後は、例の「 i = i + 1 」で、次の行に送り、ループさせてやればよい。
で、問題はループ処理の終点ですね。 今回は、「税抜価格」のところが空白になるまでというふうにしています。 なので、「Until Cells(i, 3).Value = ""」と、空の文字列を指定しています。 こうすれば、「税抜価格」さえみっちり埋まっていれば、おしりまで計算結果を入れられますからね。
ということで、解説はこのぐらいにして、動かしてみましょう※16

マクロを動かした結果
※16 マクロを動かした結果

あ。端数がでてしまった…。
でもでも、挙動は悪くなさそうですね。

<この機会にちょっとお話>
手作業だと、式を入れた後に値貼りしますよね。 でも、マクロの際は、上↑のようにマクロを書いているかぎり、値貼り付けは必要ありません。 なぜかというと、「value」、すなわち値に直接計算結果を入れているからです。 もし数式として入れたい場合は、
Cells(i, 3 + 1).Formula = "=C" & i & "*1.08"
のように、「formula」の中に数式を""チョンチョンで囲ったかたちで埋め込みます。

ワークシート関数

…すみません、端数対策のことを忘れてました。 端数はですね、丸めてやらないといけないのですが、今回は(今回も)切り捨てというルールでいきましょう。 というわけで、上のマクロにrounddownを仕込もうかと思っているのですが…、 それ以外にも、いくつか改善できるところがあると思うので、さっきのマクロをブラッシュアップしたものを作ってみました※17

税8パーセントマクロ 改
※17 税8パーセントマクロ 改

変更点は4つ。
まず①。 withを使ってまとめてみました。 この方が見やすいもんね。

②。 「空白のところまで処理する」のところの列番号を「 1 」にしました。 なぜかというと、もし「税抜価格」に空白があると、そこで処理が止まってしまうから※18です。 価格未確定なのでblancにしておくというケース、あり得ますもんね。

3列目を基準にすると、アキがあったときに、そこで処理が止まってしまう
※18 3列目を基準にすると、アキがあったときに、そこで処理が止まってしまう

なので、基準列を「 1 」にしました。 1列目は「No」列なので、ほぼ確実におしりまで何かしら値が入っていますからね。

③。 今回のキモです。
実はExcelには「ワークシート関数」と「VBA関数」の2つがあって、「ワークシート関数」というのは、われわれがいつも手作業で使ってる関数のこと。 countifやvlookupなどのことです。 rounddownもそうですね。 ワークシート上で使える関数、くらいに捉えてください。
それに対して「VBA関数」というのは、VBA特有の関数のことです。
でね、VBAから「ワークシート関数」を使うには「Application.WorksheetFunction~」というのを付けてやらないといけないんですよ。 まあ、あの、「これからワークシート関数を使いますよ~」という決まり文句だと思ってください。 なお、VBA関数を使う際にはこのような枕詞は必要なかったりします。 ちょっと不平等な感じもしますが、とりあえず、決まり文句としてApplication.WorksheetFunction、覚えちゃってください。
そこを除けば、関数の使い方はシート上と同じです。

④。マクロ処理が終了したことがわかるように、メッセージが出るようにしました。
というわけで、改良したマクロを動かすと…※19

計算終了
※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.05」→「×1.08」
※20 「×1.05」→「×1.08」

…まあ、やってることは単純なんだけど、1コだけ注意点。
今やってもらってるリストでは、「税込計算で出た端数は切り捨て」というルールで動いているんですよ。 だから、「×1.05」の値を税抜の状態に戻すとき、逆にroundupしてやらないといけない※21。 なぜなら、かつて旧税込価格を出したときにrounddownをしてたから、なんです。 きちんと逆の手順をとらないと、元の状態には戻せませんからね。 (ちなみに、僕の仲間はこの「一回roundupする」ところを理解していませんでした。だから、最終的に1円違って大あわて。)

かんたん解説
※21 かんたん解説

何だかループ処理の話題からどんどんポイントがずれてきちゃいましたが、何かの参考に。
あと、上のプロシージャ読んでもらえばわかると思いますが、「Application.WorksheetFunction~」はその都度書かなきゃいけない(今回のケースで言うと、2回書かなきゃいけない)みたいなんで、そのことも覚えておいてください。

Copyright(C)森田表計算