見出し画像

マクロのある暮らし(10回目) - あなたの愛した配列

こんにちは!なるーらぼです!昨日はお休みをいただいて広島県尾道市からしまなみ海道をサイクリングしてまいりました。
海の上なのでちょっと肌寒い感じでしたが、さわやかでしたし桜なども非常にきれいでした。

配列、お好きでしょう?

さて、今回は配列のお話しをしたいと思います。
みなさん、配列のこと大好きですよね?あれ、ちがいますか?

配列というのがなんだろうなーと思われている方のために、お話ししますと変数のように1つに1つデータが代入されているものではなくて、連続して複数のデータが代入されたものです。

こうした配列の変数には同じ種類の型(IntegerとかString)の大きさでデータが並べられます。縦横の2次元になったものも、3次元のものも作ることができます。

実はここまでの投稿でも何度も登場してきていますが、特に「これだ!」というお話はしてきていません。

配列を作るにはいくつも方法があって、一番簡単なのはVariant型の変数に「Array関数」を使って作成する方法です。

Dim arr As Variant
arr = Array(1,2,3,4,5)

上記の例では5つの数値型のデータが並んだ配列が作成されます。これらの各データを取り出したり上書きしたりするには要素番号を指定します。上記のような方法では番号はゼロから始まる決まりになっています。

Debug.Print arr(0)    '' 1が表示されます

データの数が決まった配列を作成するのであれば、変数宣言のときに指定することができますし、要素番号もゼロ以外から始めることができます。

Dim fixedarr(2 To 5) As String
fixedarr(2) = "a"
Debug.Print fixedarr(2)  '' aと表示されます

ただ、こうしたときにどこから始まってどこまであるのかがわかりません。そのために一番小さい番号を得る「LBound関数」、一番大きい番号を得る「Ubound関数」が用意されています。

Debug.Print LBound(fixedarr)  '' 2が表示されます
Debug.Print UBound(fixedarr)  '' 5が表示されます

これが2次元配列の場合は二つ目の引数として「何次元目の要素についてなのか」を数値で指定すると各次元(縦横など)の最大、最小の要素番号を得ることができます。

さらに、データの個数を知りたいときはUBound関数で得られた番号からLBound関数で得られた番号を引いて1足した数になります。
上記の例では要素番号は2から5なので「5 - 2 + 1」で4要素ということになります。

Debug.Print UBound(fixedarr) - LBound(fixedarr) + 1

ということは、要素番号がゼロから始まる場合はUBound関数の要素番号+1のデータ数を入れることができる、ということになります。

柔軟な配列

柔軟な、と言いましたが少し嘘があります。一般的なスクリプト言語に比較すると柔軟ではありません。しかしある程度の柔軟性はあります。例えば、配列に必要なデータ数がわからないことって結構あります。そうしたときには要素数不明の配列を作っておくことができます。

Dim arr() As String

この場合、必要な要素数が分かり次第「再定義」するようになります。これを実現しているのが「ReDimステートメント」です。

ReDim arr(12)

こうすると要素番号がゼロから始まって要素番号が12まで、つまりデータ数が13となる配列を定義することができます。これを繰り返すことで要素数は都度変更することができます。

ただしReDimステートメントを使って再定義すると配列は一から作り直されます。よって、それまで代入していたデータはすべてクリアされてしまうのです。

もしもデータを残したまま配列の要素数を変更したい場合はReDimに続いて「Preserve」キーワードをつけてください。そうするとデータは保存されたままになります。このとき、元よりもデータ要素数を小さく再定義するとさすがにそのときは枠から溢れる要素については削除されてしまいます。ご注意ください。

ReDim arr(2)
arr(0) = "a"
arr(1) = "b"
arr(2) = "c"
ReDim Preserve arr(1)  '' arr(2)が削除されます

これを利用すれば先頭要素や最後の要素を削除することもできますね。

さらに配列を要素数不明の状態に戻すこともできます。「Eraseステートメント」を利用すると、なかったことにしてくれます。

Erase arr

配列を便利に利用する

配列を返すものは意外とあって、組み込みの「Split関数」は分割した文字列を分割されただけ配列にして返します。CSVなどのように決まった文字で分割できるデータ要素のときは各データを配列としてアクセスすることができるので便利です。

Dim arr As Variant
arr = Split("aaa,bbb,ccc", ",")

さらにご存知でしょうか?配列ってそのままExcelの範囲としても利用することができます。

With ActiveSheet
 .Range(.Cells(1, LBound(arr) + 1) _
    , .Cells(1, UBound(arr) + 1)) = arr
End With

こうすると行方向へデータを一度にセットすることができます。

逆にRangeから配列としてデータを取り込むこともできます。

rng = ActiveSheet.Range(ActiveSheet.Cells(1, LBound(arr) + 1), _
             ActiveSheet.Cells(1, UBound(arr) + 1))
For i = LBound(rng, 2) To UBound(rng, 2)
 Debug.Print rng(LBound(rng, 1), i)
Next

上記で2次元目を取り出しているのがお分かりかと思いますが、実はExcelから配列として取り出すと行と列から構成されているため、必ず2次元配列になります。また、返されるのがRangeオブジェクトであることから、データを受けとる配列はVariantである必要があります

そこにさえ注意しておけばなんら困ることはありません。
え?行方向じゃなくて列方向にセットしたい?大丈夫です。そういうときはワークシート関数の力を借ります!

行列を入れ替えするための関数「Transpose関数」を使います。範囲として配列を扱うことができるということは、そういうことです!

With ActiveSheet
 .Range(.Cells(LBound(arr) + 1, 1), _
     .Cells(UBound(arr) + 1, 1)) _
 = WorksheetFunction.Transpose(arr)
End With

すると、意図したとおりになっているのがお分かりでしょう。

最後に

今回は配列についてお話ししました。結構便利なもので、反復と一緒に利用すると連続した操作をすることができます。ワークシートやブックも似たような方法でアクセスしますが、あれは配列ではなく「コレクション」と呼びます。少し違いますので注意してください。

ではまた!

この記事が気に入ったらサポートをしてみませんか?