最近、PythonとかRubyとかVBA以外の言語を触る機会が増えました。PythonやRubyは、配列のpush(append)が使えて非常に便利。一方、VBAの配列操作はとても不便です。
VBAの配列には、次の問題があります。
- 配列に対するpushができない
- 要素数を増やすのがめんどくさい
- 多次元配列の2次元目以降は要素数を増やせない
これらを解決するため、Excel VBAでpushを使う方法を考えました。
Excel VBAで疑似pushするクラスモジュールの作り方
実現方法は至ってシンプル。クラスモジュールにオリジナルのArray型を作成します。
Q.Man
クラスモジュール?なんやそれ!
P.Man
クラスモジュールの説明していると、超長くなるから別記事で解説するわ。(後日記事書きます…たぶん)
クラスモジュールのソースコード
クラス名は任意でOKです。私は「ClassOrgArray」としました。
Option Explicit Private isNoData As Boolean Private workBuff As Variant Public count As Long Private Sub Class_Initialize() isNoData = True End Sub Public Function push(ByRef oneDimensionArray As Variant) As Integer If isNoData = True Then '最初にpushする場合の準備作業 ReDim workBuff(0) isNoData = False Else '最初にpushした配列の要素数と引数の要素数が異なる場合は中断 If UBound(workBuff(0)) <> UBound(oneDimensionArray) Then push = -1 Exit Function End If 'WorkBuffの要素数をインクリメント ReDim Preserve workBuff(UBound(workBuff) + 1) End If 'workbuffに引数を追加(これが疑似push) workBuff(UBound(workBuff)) = oneDimensionArray count = count + 1 push = 0 End Function Public Function getArray() As Variant Dim returnArray As Variant Dim x As Long, y As Long Dim xMax As Long, yMax As Long xMax = UBound(workBuff) yMax = UBound(workBuff(0)) '戻り値要の配列作成 ReDim returnArray(xMax, yMax) For x = 0 To xMax For y = 0 To yMax returnArray(x, y) = workBuff(x)(y) Next Next getArray = returnArray End Function
push()は、1次元配列に対して1次元配列を格納しているだけ。
getArray()で、WorkBuffを2次元配列に加工しています。
実際に使ってみた。
getArray()にforの入れ子が有るので、超巨大データだと処理が終わらなさそうですね。実際に使ってみました。
▼テスト用のコード▼
Sub test1() 'クラスモジュールの宣言 Dim orgArray As ClassOrgArray Dim i As Long 'for用 Dim st As Single 'タイマー Dim reslut As Variant '戻り値 'push用の1次元配列を作成 Dim oneDarray As Variant ReDim oneDarray(99) For i = 0 To 99 oneDarray(i) = "あいうえおかきくけこ" Next Debug.Print "◆100行×100列" Set orgArray = New ClassOrgArray st = Timer For i = 1 To 100 orgArray.push (oneDarray) Next Debug.Print "push:" & (Timer - st) & "秒" st = Timer reslut = orgArray.getArray Debug.Print " GET:" & (Timer - st) & "秒" Debug.Print "" Debug.Print "◆1,000行×100列" Set orgArray = New ClassOrgArray st = Timer For i = 1 To 1000 orgArray.push (oneDarray) Next Debug.Print "push:" & (Timer - st) & "秒" st = Timer reslut = orgArray.getArray Debug.Print " GET:" & (Timer - st) & "秒" Debug.Print "" Debug.Print "◆1万行×100列" Set orgArray = New ClassOrgArray st = Timer For i = 1 To 10000 orgArray.push (oneDarray) Next Debug.Print "push:" & (Timer - st) & "秒" st = Timer reslut = orgArray.getArray Debug.Print " GET:" & (Timer - st) & "秒" Debug.Print "" Debug.Print "◆10万行×100列" Set orgArray = New ClassOrgArray st = Timer For i = 1 To 100000 orgArray.push (oneDarray) Next Debug.Print "push:" & (Timer - st) & "秒" st = Timer reslut = orgArray.getArray Debug.Print " GET:" & (Timer - st) & "秒" End Sub
▼テスト結果▼
1万行×100列の処理でpushもGETも1秒未満なので、十分実務でも使えるスピードではないでしょうか?
最後に
今回ご紹介したコードに以下のデメリットがありますので、あらかじめご了承ください。
- クラスモジュールの知識が無いと使えない
- 2次元配列のみ対応(1次元もNG)
改良する事で、多次元配列にも対応可能だと思います。皆さまのご参考になれば幸いです。
コメント