2016年7月10日日曜日

【VBAリファレンス】いっちょまえに変数を語ってみよう。

さて、このブログ開設時、最初に記事にしたのが変数でしたが
今回は変数についてもう少し掘り下げて書いてみたいと思います。

以前の記事:【VBAリファレンス】変数は宣言してください。お願いします。

先日仕事中にハッと気づいた経験談を兼ねて
書いてみます。

変数には、整数型(Long)や文字列(String)をはじめとして
たくさんの型が存在します。

しかし、使い方を間違えると、
結果としてとんでもない結果を引き起こすことがあるわけです。

中でも、数字の取り扱いには細心の注意を払っていただきたいと思います。


一番わかりやすい例をコードで記述してみます。

サンプルその1
--------------------------------
Sub test()

Dim a As Long

a = 123
MsgBox a + a

End Sub
--------------------------------

サンプルその2
--------------------------------
Sub test()

Dim a As String

a = 123
MsgBox a + a

End Sub
--------------------------------

サンプル1の結果











サンプル2の結果












サンプル1は整数型、つまり数字として扱われています。
その為、数字(123)+数字(123)でその結果246という値になります。

変わって、サンプル2に代入されているのは文字列型、
つまり、テキストとして扱われます。
その為、テキスト(123)&テキスト(123)でその結果123123と扱われます。


では、以下のサンプルの場合どうなるでしょう。
--------------------------------
Sub test()

Dim a As String
Dim b As Long

a = 123
b = a
MsgBox b + b

End Sub
--------------------------------
最初にString(文字列)型のaとLong(整数)型のbを宣言した後
aに123を代入しています。
その後、bの箱にaを代入します。
ここで、bに代入された123の型はどうなっているか…

答えはこうです。

変数とは名の通り、値が変わるものですが
代入された値は、その変数の型にならうようにされています。

数字の場合、文字としても、整数としても使用するものなので
代入されたとの変数の型に変更されます。
※つまり、最初のaの変数に"こんにちは"と代入すると、
 bの変数で型が一致せず、コンパイルエラーが発生します。

その為、数字の取り扱いには十分に注意していただきたいと思います。
データサイエンティスト系のお仕事をしていると、
特に注意が必要です。

For文を2重3重にくるくる回しまくって、
実際セルの結果として入力すべき数値とが入り組んでくると
変数の宣言も混乱してしまいがちです。
コメントなどを使って上手く変数の管理をしてみてください。


あと、変数の宣言方法について
訂正というか、この頃発見したことがあるので
合わせて記載させてください。

変数の宣言時
--------------------------------
Dim i As Long
Dim j As String
--------------------------------
こういった記述方法は基本ですが、
宣言する変数が多くなったとき1列で記述する方法についてです。

正解の宣言例
--------------------------------
Dim i As Long, j As Long
Dim x As String, y As String
--------------------------------

間違った宣言例
--------------------------------
Dim i, j As Long
Dim x, y As String
--------------------------------

宣言をつなげる場合は
変数名とAsと型とカンマで1セットとし、そのあと引き続き記述します。

間違った宣言例はどうなるかというと、
実際にLong型で宣言されているのはjのみ
String型で宣言されているのはyのみとなっており、
iとxはVariant型で宣言されたことになります。

Variant型なので、不都合が生じることはあまりないでしょうが(いや、実際にはあるかも)
きっちりと型を宣言して処理を進める癖を身に付けるためにも
横着はしないようにしましょう。


でも、変数が増えるとほんと面倒になるんですよね~。
いや、プロシージャを分けて、Callで読み込めばいいんだけど…
でも、プロシージャを分けるキリが悪いところだったりして
ついつい、長ったらしく記述したり…


みなさんはこんな悪い大人にならないように気を付けてください!

2016年7月5日火曜日

【VBAリファレンス】ステップイン機能とブレークポイントを上手に使ってデバック作業を制する。

コーディング作業にも慣れてくると
恐れることなく、バンバン長いコードを書いていけるようになります。

そうすると、いつの間にかこんなに長いコードを書いていたのかと
自分で自分を褒めたくなる半面、ちょっと怖くなります。

特に、集中して一気に記述して
いざ実行!ってボタンを押して
即エラー表示がされたりします。
私の場合よくやるのは、変数の宣言し忘れとか
If文のEndIfの記述忘れという、クソツマンネーエラーが多いので
そんな時は、周りの誰にもばれない表情で
シレっと修正してコードを実行します。

しかし、中にはエラーの原因、理由などが
よくわからなく、何度も同じ箇所でデバック対応を余儀なくされることがあります。

短いコードならまだしも、先述したとても長いコードを書いてしまったときは
恐怖を超えて狂気すら感じます。

私の場合、1回の実行で完了まで10~30分時間を要するコードを書いたり
しかも、エラーは出てないが期待している
処理結果が得られないことなどもあり、そういった場合
どこが間違っているか分からない事象が発生した場合
文字通り頭を抱えてしまいます。

毎回、スタートから完了までを通すわけには行かないので
そういったときは、まずある程度の箇所に目処を付けたうえで
ステップインで進めます。

ステップインとは、そのプロシージャの1行目から
1行ずつコードを実行していく方法です。

まず、サンプルコードです。
------------------------------------
Sub test()

Dim i As Long, j As Long

'掛け算の結果を出すFor文
For i = 1 To 9
    For j = 1 To 9
 
    Cells(j, i) = i * j
 
    Next j
Next i

End Sub
------------------------------------
実行結果は以下の通りとなります。





























これをステップインで実行する場合
コードの記述まで行った後
画面の開発のタブマクロ作成したプロシージャ名を選択ステップ インをクリックします。




























そうすると、VBE画面が表示され、
現在の進行箇所が黄色く表示されます。
そして、次の行に移動するにはファンクションの8番(F8キー)を押します。

実行最初の画面


F8キーを1回押した状態


F8キーを2回押した状態


F8キーを押しっぱなしにすると、
ものすごいスピードでブルブルン!って実行状態を見ることが出来ます。

一つ一つの実行結果を確認するには
もってこいの機能です。
もちろん、Excelの画面を開きながらやれば
もちろん画面反映を確認しながら進めることも出来ます。

また、Forの動きなどは、
このステップインを見ていると、どういった動きをしているのか
目で確認できるので、非常に良いと思います。


ただし、先述したとてつもなく長いコードを記述した際
しかも、確認したい箇所は最後の方
その上、それまでの箇所にFor文を連掛けしているなど
不都合の女神に微笑まれた場合は
任意の場所までは一気に実行するという方法もあるのです。

それはブレークポイントの設定です。

下図赤枠内ジャンプさせたい列辺り


クリックすると、赤黒い●マークがつきます。


ステップイン実行後、このブレークポイントを実行すると
その箇所までは、通常のコードを実行したスピードで
コードが実行されます。

その後、F8を押せば、1行ずつステップイン作業が実行されます。
その後、すべてのコードを実行する場合は
F5(実行ボタン)か四角のリセットボタンを押せば
作業の継続かストップを選択できます。

是非、この機能を活用して無駄のないデバック作業を行ってください。

私の場合、デバック作業のいらない完璧なコードばかりではなく
たくさんのエラーを経験した方が
よりよりコードが書けるようになると信じてます。



2016年6月26日日曜日

【VBAコラム】VBAコードを記述した方がいい時としない方がいい時

プログラミングの世界では
ある物を作るとき、全く無の状態から作成されていきます。
そんな時に、何をどう作っていくのか、細かく書かれている説明書のようなものを
仕様書といいます。

大きなソフト開発プロジェクトでは、必ずそういった仕様書がありますが
個人的にExcelのデータ操作を行う場合に記述するVBAでは
そこまで大げさなものもありません。


そもそもどうしてVBAのプログラムまで覚えて、コーディングして、マクロを実行するのでしょう。

と、同時に現在VBAの勉強に行き詰っている人がいたとすると、
行き着く答えの出口は意外と同じなんてこともあります。

つまり、目的です。

どういう動作をエクセルにさせたいか。

VBAのプログラミングを行うときに
一番重要な箇所、それが仕様です。


表題の件に関してですが
VBAのコードを記述した方がいい時と、記述しない方がいい時
こればかりは、ギブアンドテイクの天秤で測るしかないのですが
A1からA10までに数値が入っていて、その合計をA11に表示させるのであれば
VBAでコードを記述するよりA11のセルに=SUM(A1:A10)と関数を入れてしまった方が
早いわけなんです。

では、こんな内容はどうでしょう。

A1からA10までのセルに1~100まででランダムの数値が入っていたとします。
A11には合計を入力し、B1~B10の間でそのランダムの数値が
1~50までは黒い文字、51~100までであれば赤い文字にする。
なお、ブック内に同様のシートが5000シート分あります。
そうなれば、手作業で行うより、VBAを記述した方が早いでしょう。

手で動かして動作させても、VBAコードを記述して
処理を自動化させても、結果は同じことですが
どのように進めていくかが仕様になります。


なにもVBAは作業の効率化だけではありません。
確かに、膨大なデータをボタン1クリックで
あっという間に処理を行ってくれるという機能は
誰しもご存知かと思います。

しかし、その裏で作業を行うのはコンピューターであり
仕様さえ間違いがなければ
決定的なエラーが1つなくなります。
それがヒューマンエラーです。

どんなに頑張っても、
ミスの少ない人はいても、ミスを絶対にしない完璧な人はいません。
ヒューマンエラーは人間が引き起こす
仕様書の欠落です。

単純作業ほど回数を重ねれば
人間は精度を落とし、いつかエラーを起こします。
しかし、コンピューターはそういったことは絶対にしません。

私が、仕事中で悩む最初の分岐は
自分で処理を行うか、コンピューターに処理を行うかの
選択になります。

簡単にIf文で例えてみます。

-------------------------
If me =>-5 Then
    VBAコード入力を開始
Else
  自分で処理を行う
-------------------------
という文になります。

これは勝手な印象値になりますが
僅かに処理結果にマイナスが発生したとしても
大幅なマイナスにならないようであれば、
VBAでコーディングして処理を実行させるようにします。

もう少し、具体的にいうと
手作業の場合は3時間かかる、
VBA入力の場合はコーディングから実行まで4時間かかるとしたとき
私はVBAのコーディングを選択するようにしてます。

理由は2つあります。
■先述した通り、コンピューターは指示通りの作業をこなすので
記述したコードが間違いなければ処理ミスはありえない。
■手作業で行った処理で処理ミスが発生した場合
最悪もう一度最初からやり直す必要があり、結果倍の6時間を要する。

メリットは他にもあります。
VBAに対する知識や発想力は、たくさんのテーマをクリアすればするほど豊富になり、
さらに次への対応の幅が広くなります。
もちろんデメリットもあります。
VBAの仕様コーディングに誤りがあり
予想していた以上にコーディングに時間がかかってしまうこともあります。

ただし、ここでのデメリットはメリットの経験を重ねれば重ねるほど
どんどん小さくなってきます。

やはり、やるに越したことはないわけです。
少々のロスは承知の上で、果敢にVBAコーディングに挑んだ方が
返ってくるメリットは大きいといえるでしょう。



2016年6月19日日曜日

【VBAリファレンス】ワークシートの作成、任意の名前のワークシートの作成、任意の名前のワークシートを作成し移動させる。の3本立てです。

たくさんのデータを取り扱っていると
ワークシートを追加した上で
新たな処理を行う必要性が出てきます。

ワークシートを追加する場合の
基本コードは以下の通りです。

-------------------------------------------
Sub ワークシートを追加()

Worksheets.Add

End Sub
-------------------------------------------
この1行を記述するだけで
ワークシートを追加できます。
Excelのバージョンによりますが
2013以降はデフォルトのワークシート数がSheet1のみとなっており
以前はSheet1~3までとなっています。

上記のコードを実行すると
以降のシート番号が自動で割り振られ
シートが生成されます。

※処理結果


















ただし、VBAコードで処理を実行するとき
シートを指定したうえで、作業を行うことがあり
時に、Sheet番号を指定してするにはちょいと面倒だったりします。
そんなのいちいち覚えてられませんし。
余談ですが、Exceブックlを開いたままシートを生成すると
生成した数だけシート番号が上がっていきます。
しかし、ブックを開きなおすと、またそのシート番号は最初のSheet1から生成され始めます。
といっても、ブック内にSheet1があると飛ばされSheet2から生成されます。
(ここは覚えなくていです)


さらに横道にそれますが
シートを指定して処理を行う場合、
以下のように記述してワークシートを指定します。
-------------------------------------------
Sub ワークシート処理()

Sheets("指定したいシート名").Select

End Sub
-------------------------------------------
赤文字の箇所を指定したいシート名に変えて実行すると
そのシートを指定することが出来ます。

さて、ではシートを作成した時に任意のシート名を記述しておけば
希望通りの処理を行うことが出来ますね。

一連の処理を記述します。
・【新しいシート】という名称のワークシートを作る
・そのシートを指定する
・そのシートに算数の九九の結果をFor文を使って記述する

上記3つの処理を1つのプロシージャで作成してみます。
なお、シート名をつけたい時は
Worksheets.Addの次の行に
ActiveSheet.Name = "指定したいシート名"と記述するだけでOKです。


まずは、実行前のブックの画像です。


なんてことはない普通のシートです。

記述するコード画像です。


記述している内容は以下の通りです。
-------------------------------------------
Sub test()

Dim i, j, x As Long

'新しいシートを生成し、シート名を新しいシートに変更する
Worksheets.Add
ActiveSheet.Name = "新しいシート"

'新しいシートを選択する
Sheets("新しいシート").Select

'新しいシートに九九の表示を行う
For i = 1 To 9
    For j = 1 To 9
    x = j * i
    Cells(j, i) = x
    Next j
Next i

End Sub
-------------------------------------------

実行後の画像


新しいシートが作成され、九九の結果が記述されています。



では、もう少し詳しく説明させてください。

作成時シートを任意の場所にあらかじめ移動しておきたい場合は
以下のように記述します。
-------------------------------------------
Sub ワークシートを追加()

Worksheets.Add after:=Worksheets(Worksheets.Count)

End Sub
-------------------------------------------
これは作成したシートを
まず、ブック内にあるシートの枚数をしらべます※赤文字の部分です。
で、そのシートの後(Afterの部分)に移動をさせるという記述です。

カッコ内のWorksheets.Countの部分はシート名で指定することが出来ます。
例):新しいシートというシートの後に移動させたい場合は
Worksheets.Add after:=Worksheets("新しいシート")となります。
シート名を指定する場合は、ダブルクォーテーションで囲ってあげてください。


また、指定しているシートの前に生成したい場合は
afterbeforeに変えてあげれば、指定したシートの左隣に移動されます。
右隣がafterで左隣がbeforeと覚えておきましょう。

※シートの作成時に特に指定をしない場合は、
一番左側に新しいシートが生成されますので合わせて覚えておいてください。


2016年6月16日木曜日

【VBAリファレンス】InputBoxは他の誰かのために使いましょう。

今日はInputBoxについて記事を書いていきたいと思います。

さて、世の中にたくさんあるツールの中で
何かの処理を行う場合、
とある入力を求められることがあります。

こういうやつです。


インプットボックスっていって
使用するユーザーに任意の情報を入力するための
テキストボックスになります。

記述自体はとても簡単です。
-------------------------------------------------
Sub test()

'ダブルクォーテーションの間のメッセージを変えるだけで
'表示するメッセージを変えられます
Application.InputBox ("名前を入力してください")

End Sub
-------------------------------------------------
上記、ウィンドウを表示させるにはこのコードを記述するだけで
出現させることが出来ます。

しかし、これだけではウィンドウが出るだけで
文字を入力してもなーんの意味もありません。

InputBoxって、文字を入力して
その入力した文字を何かしらの処理の材料として使いたいわけですよね。

なので、右辺を左辺に代入するということで
変数を使用します。

Application.InputBoxの前に、宣言済みの変数をイコールで
つなげてあげましょう。
-------------------------------------------------
Sub test()

Dim ipt_b As String

ipt_b = Application.InputBox("名前を入力してください")

End Sub
-------------------------------------------------
変数名ipt_bとします。
で、今回は入力する値が名前なのでテキスト型(String)を宣言します。
※当然ですが値が数値の場合は、Long型を宣言してあげてください。

これを実行すると、InputBoxで入力した文字列が
変数ipt_bに代入されます。

さて、これだけでも意味がありません。

あとは、変数に代入した値をどう使うかは皆様次第です。


例えば、列の処理をする場合は
String型として、直接列の値を入力して見るのもいいかもしれません。


任意の列を削除したい時
以下のような使い方が出来ます。
-------------------------------------------------
Sub test()

Dim ipt_b As String

ipt_b = Application.InputBox("削除したい列を入力してください")
Columns(ipt_b).Delete

End Sub
-------------------------------------------------

サンプル画像(C列を削除したいと思います)


先ほどのコードを実行します。
InputBoxにCと入力しOKを押すとコードが実行されます。










ここでは文字列の【C】が
変数ipt_bに代入されます。
そして、Columns(ipt_b).Deleteに充てられるので
Columns(C).Deleteと同じ意味になりますね。

処理結果




C列にあった【この列は消す】という列が消えています。


InputBoxはおもに、他の誰かから情報を取得する場合、
あるいは、都度変わる任意の情報を直接入力する場合
に大変役立つ機能です。


この使用例はほんの一部ですので、色々と試してみてください。

2016年6月14日火曜日

【VBA実践】特定の列を一括挿入する。

別の記事で
特定の列を削除する内容をご案内しました。
【VBA実践】特定の列を一括削除する。
今回は作業を行うため、空の列を挿入するコードを記述します。
基本は列を削除する内容とあまり変わりません。
For文を駆使して行っていきます。

サンプル画像
 1から10(AからJ列)までデータが入っており
その各列に空の列を挿入します。

コードは以下の通り。

削除するコードと基本は同じ逆打ちです。
-------------------------------------------------
Sub 特定の列を挿入するコード()

Dim col As Long

For col = 10 To 2 Step -1
    Columns(col).Insert
Next col

End Sub
-------------------------------------------------
1列目までは挿入する必要はないので
カウンタは10 To 2 になります。
また、Stepは-1になります。
処理分は挿入のInsertです。

実行後画面

上手く実行できたことが確認できました。



逆打ちではなく、通常カウントで実行できなくもないのですが
やはりそのやり方はおすすめできません。

試しにサンプル実行してみます。
※Excelのスタート画面は同じレイアウトとします。

コードは以下の通り。

カウント値を逆にしStepは 2とします。

結果は以下の通りです。

上手くいっているように見えますが
よく見てみると、様子が変です。

右方向にスクロールしてみます。

6以降が挿入されていません。

勘のいい方はすぐにお分かりいただけたかと思いますが
カウンタが10となっていることとStep 2となっているところがポイントです。

2列目から挿入されステップで2飛ばしとなって
最後は10で終了しているカウントということは
J列が最後の列になります。
この場合の対応方法は通常の倍のカウント数にしなければいけないということです。

なので、直感的に処理を行うのであれば
やはり前者にご覧いただいた
カウント逆打ちが一番いいですね。

また、データが入っている最終列の値を出すコードを
合わせて組み込み、Forの逆打ちの最初のカウントに代入しておくだけで
後はVBAが勝手に処理してくれます。

コードはただ書くだけではなく
無駄なく処理が実行されることも重要です。

ぜひ活用してみてください。

【VBA実践】特定の列を一括削除する。

今回は不要となった特定の列を削除するコードをご紹介します。
列を挿入するコードは別の記事にてご案内していますので
是非参考にしてみて下さい。
【VBA実践】特定の列を一括挿入する。

大量のデータを処理をしていると
A列に大元のデータがありB列にA列のデータを処理した結果を入れたりします。
また、それが、C列以降、同様の列が続いたりします。

例えば、データ処理が終わった場合
不要となった列を削除したりすることがあります。

間が飛び飛びになっている場合は、
一つ一つの列を手作業で削除していくのは大変骨が折れる作業です。
また、誤って消すつもりのない列を消してしまったりする
大変なことになります。


そんな作業も、ちょっとコードを入力するだけで
正確かつ一瞬で終了します。

サンプル画像

























まずは偶数(赤文字)が記載されている列を一括で削除するコードです。
































上記コードを実行すると以下の結果がでます。


-------------------------------------------------
Sub 特定の列を削除するコード()

Dim col As Long

For col = 100 To 1 Step -2
    Columns(col).Delete
Next col

End Sub
-------------------------------------------------
まず変数は、列を意味するcolumnの最初の3文字を取って
変数名colにします。
colはFor文のカウンタの役割を行うので
値を代入しておく必要はありません。

次にFor文では逆打ちカウンタでStepは-2となります。

まず、なぜ逆打ちにするかということですが
通常の2..4..6..8..10とカウントしていった場合
削除する列がおかしくなってしまうんです。
例えば、最初に2があるB列を消すと、隣のC列にあったデータがB列になります。
ということは、次のカウント4列目つまりD列には
E列に入っていたデータを消してしまうことになります。

サンプル画像1
































上記コードを実行すると以下の結果となります。























手前から消すと先のデータは前にずれてくるので
全ての結果がずれてきます。

その為、データの最後から実行していくことで
ターゲットの列データがずれることはなくなります。
こういった逆打ちでデータを操作する方法は、VBAの処理ではよく使用されるので
頭の片隅に置いとていただけると幸いです。




ちなみに、Columns(col).Deleteは削除です。
ClearContentsやClearにすると、列の削除はされず、値や値と書式のみが削除されます。

サンプルコードの画像
































実行後の画像

という結果となります。

A、B、Cを1ブロックとして、そのB、C列を削除したいという
2列消しをする場合も要領は同じとなります。
サンプル画像

赤くなっている文字の列を削除する場合のサンプルコードは以下の通りです。


削除する列は2列に増えるので
Columns(col).Delete(指定している列)と
Columns(col -1).Delete(指定した左隣の列)を合わせて記述し削除します。
(col -1)というのは、変数colに代入されている値をマイナス1にするという記述です。
※結構こういった使い方することがあるのでぜひ覚えておいてください。
なお、2列消して、3列戻るので、Step -3としてください。

実行後の画面


上手く実行できたことが確認できました。


さて、別の記事でも今後記載予定ですが
先日、データが入っている最終行を取得するというコードを案内しましたが
最終列を取得するコードも当然あります。


column = Cells(1, Columns.Count).End(xlToLeft).Column

変数名columnとして、そこにデータが入っている最終列の値を代入するようにします。
Cells(1,~~の1は1行目を指定する箇所であり、
データが入っている行をここで入力します。
なので、1列目にデータが入っていないときは、データが最終列まで入っている行を
ここで入力してあげてください。
そして、その変数columnをカウンタに設定してあげます。

-------------------------------------------------
Sub 特定の列を削除するコード()

Dim col As Long
Dim column As Long

column = Cells(1, Columns.Count).End(xlToLeft).Column
For col = column To 1 Step -2
    Columns(col).Delete
Next col

End Sub
-------------------------------------------------
最終列が100行まで入っている場合、
変数columnには100が代入されるので
For col = 100 To 1 Step -2と同じ意味になります。
変数の宣言も忘れず行ってください。


ちょっと長い記事になってしまいましたが、
状況に応じてぜひ活用してみてください。