Excelマクロ(VBA)プログラミング

Excel VBAで配列のpushを実現してみた

Excelマクロ(VBA)

最近、PythonとかRubyとかVBA以外の言語を触る機会が増えました。PythonやRubyは、配列のpush(append)が使えて非常に便利。一方、VBAの配列操作はとても不便です。

VBAの配列には、次の問題があります。

  • 配列に対するpushができない
  • 要素数を増やすのがめんどくさい
  • 多次元配列の2次元目以降は要素数を増やせない

これらを解決するため、Excel VBAでpushを使う方法を考えました。

Excel VBAで疑似pushするクラスモジュールの作り方

実現方法は至ってシンプル。クラスモジュールにオリジナルのArray型を作成します。

Q.Man
Q.Man

クラスモジュール?なんやそれ!

P.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:" &amp; (Timer - st) &amp; "秒"
    st = Timer
    reslut = orgArray.getArray
    Debug.Print " GET:" &amp; (Timer - st) &amp; "秒"


    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:" &amp; (Timer - st) &amp; "秒"
    st = Timer
    reslut = orgArray.getArray
    Debug.Print " GET:" &amp; (Timer - st) &amp; "秒"


    Debug.Print ""
    Debug.Print "◆1万行×100列"
    Set orgArray = New ClassOrgArray
    st = Timer
    For i = 1 To 10000
        orgArray.push (oneDarray)
    Next
    Debug.Print "push:" &amp; (Timer - st) &amp; "秒"
    st = Timer
    reslut = orgArray.getArray
    Debug.Print " GET:" &amp; (Timer - st) &amp; "秒"


    Debug.Print ""
    Debug.Print "◆10万行×100列"
    Set orgArray = New ClassOrgArray
    st = Timer
    For i = 1 To 100000
        orgArray.push (oneDarray)
    Next
    Debug.Print "push:" &amp; (Timer - st) &amp; "秒"
    st = Timer
    reslut = orgArray.getArray
    Debug.Print " GET:" &amp; (Timer - st) &amp; "秒"
    
    
End Sub

テスト結果▼

1万行×100列の処理でpushもGETも1秒未満なので、十分実務でも使えるスピードではないでしょうか?

最後に

今回ご紹介したコードに以下のデメリットがありますので、あらかじめご了承ください。

  • クラスモジュールの知識が無いと使えない
  • 2次元配列のみ対応(1次元もNG)

改良する事で、多次元配列にも対応可能だと思います。皆さまのご参考になれば幸いです。

コメント

タイトルとURLをコピーしました