配列変数を使おう。

プロシージャで処理を行うとき、変数を使うことは日常茶飯事です。
しかし、まとまりのあるデータを扱う場合は、配列変数を使うとマクロが組みやすくなります。

1.配列変数を宣言する。

まず、下のプロシージャを比較してみましょう。
上のプロシージャでは、教科名を普通の変数に入れていますが、下のプロシージャでは、配列変数に入れています。
配列変数のいいところは、呼び出したり、書き込んだりするときに、数値で指定できることです。
このことによって、いろいろなデータを読み出しやすくなります。

Sub test01()

  Dim kokugo As String ←@
  Dim syakai As String
  Dim sansuu As String
  Dim rika As String

  kokugo = "国語" ←A
  syakai = "社会"
  sansuu = "算数"
  rika = "理科"

  MsgBox kokugo ←B
  MsgBox syakai
  MsgBox sansuu
  MsgBox rika

End Sub 
@ 必要な数だけ変数を宣言しています。(データ型は文字列型です)

A 変数1つ1つに代入しています。

B 変数の内容を表示するのに、1つ1つ指定しています。
Sub test02()

  Dim 教科(3) As String ←@
  Dim n As Integer

  教科(0) = "国語" ←A
  教科(1) = "社会"
  教科(2) = "算数"
  教科(3) = "理科"

  For n = 0 To 3
    MsgBox 教科(n) ←B
  Next n

End Sub
@ 教科(3)と、配列変数で宣言しました。
  配列のインデックス番号は、通常0から始まるので、この場合は
  教科(0),教科(1),教科(2),教科(3)
の4つの変数を宣言したことになります。

A 個別に代入していきます。

B 先ほどのマクロと違って、For〜Next文を使い、すっきりとまとまりました。
  nの値が、0から3まで順に変わっていくので、4つの変数の内容を順番に表示します。
配列変数は、教科(2,3)というようにも宣言できます。この場合に使える変数は、
 教科(0,0) 教科(0,1) 教科(0,2) 教科(0,3)
 教科(1,0) 教科(1,1) 教科(1,2) 教科(1,3)
 教科(2,0) 教科(2,1) 教科(2,2) 教科(2,3)
の12個になります。これを2次元配列変数といいますが、同じように教科(2,3,4)というように3次元以上の配列を作ることも可能です。
しかし、現実的には、1次元配列か、2次元配列までをよく使います。

2.インデックス番号の下限値と上限値を設定する。

配列変数のインデックス番号は、任意の範囲で設定することができます。

Sub test03()

  Dim 教科(6 To 9) As String ←@
  Dim n As Integer

  教科(6) = "国語" ←A
  教科(7) = "社会"
  教科(8) = "算数"
  教科(9) = "理科"

  For n = 6 To 9 ←B
    MsgBox 教科(n)
  Next n

End Sub 
@ 今度は、教科()の中を、6 To 9 としました。
  これで、作られた配列は、
  教科(6)、教科(7)、教科(8)、教科(9) の
  4つになります。

A もちろん、代入するときの範囲も6〜9です。

B 読み出すときも、6〜9になります。
  左のようなワークシートから、
データを配列変数に読み込むときには、
左下のように行います。
 


左下のマクロでは、1列目の教科名を配列に読み込んでいます。
Sub test04()

  Dim 教科(1 To 8) As String ←@
  Dim R As Integer

  For R = 1 To 8
    教科(R) = Cells(R, 1) ←A
  Next R

  For R = 1 To 8
    MsgBox 教科(R)
  Next R

End Sub 
@ 1行目から8行目までのデータを読み込むので、
  それに合わせて、1 To 8とします。

A セルの1列目のデータを1行目から順番に配列変数に格納します。
 
要素番号の変更は、2次元以上の配列でも、使えます。
例えば、教科(1 To 3 , 1 To 3 , 2 To 5)のようなこともできます。

3.動的配列を使う。

1で扱った配列は,あらかじめ決まった大きさ(個数)の宣言するので,静的配列といいます。
しかし,場合によっては,データ数がわからず,データ数を調べてから宣言したいときもあります。
こういうときは,大きさは宣言せず,変数の型だけを宣言します。そして,あとから大きさを決めます。
この配列を,動的配列と言います。

Sub test05()

  Dim 教科() As String ←@
  Dim n As Integer

  ReDim 教科(3) ←A

  教科(0) = "国語" ←B
  教科(1) = "社会"
  教科(2) = "算数"
  教科(3) = "理科"

  ReDim Preserve 教科(7) ←C

  教科(4) = "音楽" ←D
  教科(5) = "図工"
  教科(6) = "家庭科"
  教科(7) = "体育"

  For n = 0 To 7
    MsgBox 教科(n)
  Next n

  ReDim Preserve 教科(4) ←E

  For n = 0 To 4
    MsgBox 教科(n)
  Next n

End Sub 
@ 動的配列変数を宣言するときは、インデックス番号を省略します。

A データの数が決まったら、ReDimステートメントで再宣言します。
  データ型は変更できないので、文字列型のままです。

B データを代入します。

C データを保持したまま、配列を拡張したいときは、ReDimのあとに
  Preserve を付けて再宣言します。

D 4〜7の変数に、新たにデータを入れます。0〜3までのデータは保持されています。

E 同じように、ReDim Presarve で、先ほどの要素数より少ない数で宣言したときは、
  はみ出した部分のデータは切り捨てられ、残りのデータは保持されます。



  Preserveを使わないで、ReDimだけで宣言すると、配列の内容は初期化されてしまいます。
ここで、気をつけなければいけないのは、教科(2,)のような2次元以上の配列を宣言したときです。
ReDimで変更できるのは、のように一番末尾の部分だけです。それ以外の場所はできません。
※ 静的配列と動的配列では,「変数が使えるか?」でも違いが出てきます。
Sub 静的配列()

  Dim n As Integer
  n = 3
  Dim 配列(n) As Integer ←@

End Sub
左の例は,@のところで変数を使って宣言をしようとしています。
実行すると,「定数式が必要です。」とエラーメッセージが出てしまいます。

定数式の意味ですが,nを2*3のようにすると正常に実行されることから,
完全に固定された数値ならいいようです。

また,下のようにユーザーが定義した定数も使えるようです。
Const n As Integer = 3
Dim 配列(n) As Integer 
Sub 動的配列()

  Dim n As Integer
  Dim 配列() As Integer
  n = 3

  ReDim 配列(n) ←A

End Sub 
Aで変数を使っていますが,動的配列なので正常に実行されます。
実際は,データの個数を調べて,その個数で宣言することが多いでしょうね。

4.Array関数を使って、入力の手間を減らす。(1次元配列)

配列変数にデータを格納するとき、今までは1つ1つ代入してきましたが、少ない個数であれば一気に格納する方法があります。
それが、Array関数を使う方法です。

Sub test06()

  Dim 教科 As Variant ←@
  Dim n As Integer

  教科 = Array("国語", "社会", "算数", "理科", "音楽", _
          "図工", "家庭科", "体育")
 ←A

  MsgBox LBound(教科) & "〜" & UBound(教科)  ←B

  For n = LBound(教科) To UBound(教科) ←C
    MsgBox 教科(n)
  Next n


End Sub
@ Array関数を使って配列に格納する時は、必ず、データ型をVariant型にします。

A 今回のデータは文字列なので、”(ダブルクォーテーション)で囲み、1つ1つのデータの区切りは,(カンマ)で区切ります。数値の場合は、”(ダブルクォーテーション)はいりません。
  この1行で、変数「教科」に配列として、8つの教科名が入りました。

B 参考までに、インデックス番号がいくつから(下限)、いくつまで(上限)になっているか表示させています。今回の場合、0〜7になっています。

 下限 LBound(教科)    上限 UBound(教科)


C For〜Next文で、教科(0)〜教科(7)を表示させています。
Sub test07()

  Dim 教科 As Variant
  Dim tmp As Variant ←@

  教科 = Array("国語", "社会", "算数", "理科", "音楽", _
          "図工", "家庭科", "体育")


  For Each tmp In 教科 ←A
    MsgBox tmp
  Next tmp

End Sub
@ For Eachを使いたいので、バリアント型で宣言します。

A ここから、For Each文を使っています。
  変数の内容を取り出すだけなら、こういうやり方もできます。

5.ワークシートのデータを、配列変数に一気に格納する。(2次元配列)

同じように、ワークシートにあるデータも一気に配列に格納する方法があります。
やりかたは、Array関数とよく似ています。
この方法は、読み込みも書き込みも速いので、高速化には欠かせません。

   
Sub test08()

  Dim 文字 As Variant ←@
  Dim R As Integer
  Dim C As Integer

  文字 = Range("a1:d8") ←A

  For C = 1 To 4
    For R = 1 To 8
      文字(R, C) = "★" & 文字(R, C) ←B
    Next R
  Next C

  Range("a1:d8") = 文字 ←C

End Sub
@ 一気に変数に格納するので、Variant型で宣言します。

A ここで、範囲内のデータを、一気に2次元配列として格納します。
  文字(R,C)のRは行、Cは列になります。
  つまり、Cells(R,C)と同じように扱うことができます。

B 配列に入っているデータの前に、★を付けます。

C ここで、一気にセルに書き込みます。
ワークシートから配列変数に一気に格納する場合は、開始値は1からになります。
また、どんな場合も2次元配列になります。

例えば、
A1〜A10 までを格納した場合は、A( 1 to 10 , 1 )と同じですし、
A1〜G1  までを格納した場合は、 A( 1 , 1 to 7 )と同じです。

ここで,インデックス番号の上限と下限を求めるには,次のようにします。
下限 LBound( 文字 , 1 )   上限 UBound( 文字 , 1 ) ・・・行方向の上限と下限が求まります。
下限 LBound( 文字 , 2 )   上限 UBound( 文字 , 2 ) ・・・列方向の上限と下限が求まります。

6.インデックス番号の開始値を1に設定する。

配列変数は、通常、0から始まります。しかし、セルの行や列は1から始まるので、時にはこれと一致させておくと便利です。
そして、要素数とインデックス番号が一致するので、わかりやすいのも利点でしょう。
設定するのは、「Option Base」ステートメントを使います。
このステートメントは、モジュールの先頭で使います。
また、これを設定すると、このモジュール内の配列変数全てに適用されます。

   
 (Option Explicit は、変数の宣言を強制するステートメント)

こちらは、通常通りなので、下限が0になっています。


要素数は、
配列(0)、配列(1)、配列(2)、配列(3) の4つです。
  (Option Explicit は、変数の宣言を強制するステートメント)

こちらは、「Option Base」ステートメントを使っているので、
下限が1になっています。

要素数は、
配列(1)、配列(2)、配列(3) の3つです。
 Option Base ステートメントでは、0か1を設定します。デフォルト状態が0なので、使うときは1でしょうね。
ちなみに、上限を調べるときは、UBound を使います。

7.その他、配列変数に関わる関数(1次元配列)

Sub test09()

  Dim 教科(7) As String ←@
  Dim 項目 As String

  教科(0) = "国語" ←A
  教科(1) = "社会"
  教科(2) = "算数"
  教科(3) = "理科"
  教科(4) = "音楽"
  教科(5) = "図工"
  教科(6) = "家庭科"
  教科(7) = "体育"

  項目 = Join(教科, ",") ←B

  MsgBox 項目

End Sub 
@ 配列変数を文字列型で宣言します。

A 配列にデータを代入します。

B Jion関数で、配列内のデータをカンマでつないで、1つのデータにします。

Sub test10()

  Dim 項目 As String
  Dim 教科 As Variant ←@
  Dim tmp As Variant

  項目 = "国語,社会,算数,理科 ,音楽,図工,家庭科,体育" ←A
  教科 = Split(項目, ",") ←B

  For Each tmp In 教科 ←C
    MsgBox tmp
  Next tmp

End Sub
@ 配列変数として使いたいので、バリアント型で宣言します。

A 項目という変数に、文字列を代入します。,(カンマ)が使われてはいますが、”(ダブルクォーテーション)で両端がくくられているので、このカンマもデータの一部です。

B Split関数で、,(カンマ)でデータを分割して、配列変数に代入します。

C ここで確認すると、ちゃんと配列になっていることがわかります。
Join関数とSplit関数は、CSVファイルの読み書きをするときに知っていると便利です。
書き込み・・・セルのデータを配列に読み込み、Join関数でつないで1行分のデータを作り、CSVファイルとして書き込みます。
読み込み・・・1行分のデータを読み込み、Split関数で配列に分割し、各セルに読み込みます。

2010では、CSVへの書き込みは正常にできますが、CSVを読み込むときにこちらの意図しない形に加工されてしまいます。
例) 01が1になったり、1E13が指数と認識されたり。

そこで、Line Input を使って読み込み、
Split関数を使って1行分のデータを分割し、セルに代入するようにすると、そのまま加工されずに値が入ります。
このとき、「セルの書式設定」は、表示形式を「文字列」にしておいてください。
そうしないと、数値と見なして、数値に変換してしまうので、元の木阿弥です。
Join関数を扱う時には,注意することがいくつかあります。
@1次元配列なので,Variant型の変数にセルの値を一気に格納した配列では,使えません。
AJoin関数が扱える配列変数は,文字列型かVariant型だけです。それ以外の変数では,エラーになります。

8.変数の中身を見る。

マクロを組んでいて処理が複雑になってくると、思ったような結果が得られないことが多々あります。その原因を探るために、変数の内容がわかると非常に便利です。MsgBoxを使って、処理ごとに表示させる方法もありますが、ローカルウィンドウを使うと全部の変数の内容を見ることができます。

  デフォルトでは、マクロを書き込む画面の下に、イミディエイトウインドウとローカルウィンドウが表示されています。
 
ローカルウィンドウは、そのときの変数の内容を表示します。

このときに注意しなければいけないのは、処理が終わってしまうと変数も初期化されてしまうので、途中でプロシージャを止める必要があるということです。


左の例では、プロシージャの最後に、Stopメソッドを入れています。
これで、実行すると、Stopのところでプロシージャが一時的に停止します。
すると、全ての変数の内容を見ることができます。


処理の続きを行うときは、マクロの再生ボタン『Sub/ユーザーフォームの実行』を押します。そのまま終了するときは、停止ボタン『リセット』を押します。
  プロシージャを止める方法として、ブレークポイントを設定する方法もあります。


左の図で●(茶色)と表示されている部分がありますが、これはマウスでこの場所をクリックするとなります。
つまり、この場所でプロシージャを止めることができるのです。
解除するときは、をもう一度クリックすると、消えます。

ブレークポイントを設定して、プロシージャを動作させたときは、停止ボタンを押してからでないと、解除できませんので気をつけてください。

9.配列変数を初期化する。

今まで配列変数に代入することばかり書いてきましたが,実際には,ループ処理の中で,「代入して処理して初期化して,代入して処理して初期化して,・・・」と,同じ変数を使うことも出てきます。
配列変数の初期化は,次のようにします。

Sub test11()

  Dim 数値(4) As Integer
  Dim n As Integer

  For n = 0 To 4
    数値(n) = n ←@
  Next n

  For n = 0 To 4
    MsgBox 数値(n) ←A
  Next n

  Erase 数値 ←B

  For n = 0 To 4
    MsgBox 数値(n) ←C
  Next n

End Sub
 
@ここで,配列変数「数値」に値を代入します。
 以下のように,データが格納されました。
  数値(0) = 0
  数値(1) = 1
  数値(2) = 2
  数値(3) = 3
  数値(4) = 4

Aここで,入っているデータを表示させて,確認してみます。

Bここで,配列変数の初期化をしています。

C内容を確認すると,全部0(初期の値)になっています。