最近、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)
改良する事で、多次元配列にも対応可能だと思います。皆さまのご参考になれば幸いです。


コメント