Hello! Mr.Simplism

僕の人生補完計画

【EXCEL VBA】二次元配列の行の削除はできない。別の配列に「削除しない行」だけコピーして間接的に行の削除をする方法

f:id:yohsuke517:20220213213527p:plain

Excel VBAは難しいけど楽しい

例えばシート上にあるデータベースを二次元配列に入れ、条件に合った「行」を削除して
別のシートorセルにその配列を貼り付けようとしたとき、EXCEL VBAの二次元配列上では行の削除はできないということが分かった。

EXCEL VBAでは二次元配列の列の削除は出来ても行の削除はできないようになっているらしい。

 

2つの配列を使う方法

そこで、単一の二次元配列で無理なのであれば、すべてのデータを読み込む配列①と、必要なデータのみをコピーした配列②の2つの二次元配列を用意する方法を思いついたので、実際に動かしてみた。

 

結論から言って、うまくいった。

 

まず、このようなデータを用意した。

f:id:yohsuke517:20220213213659p:plain

1行目の見出しも含めて11行×3列のデータになっている。
今回はB列の値が「不要」の行を配列上で削除してみる。

 

コード全文

今回書いたコードは下記の通り。

Dim arrBf() As Variant
Sub array_RowDelete()

Dim wS As Worksheet

    Set wS = ThisWorkbook.Sheets("Sheet1")

Dim arrBf() As Variant  '全データを入れる二次元配列
    
    ReDim arrBf(11, 3)
    arrBf = wS.Range("A1:C11").Value
    
Dim arrAf() As Variant  '必要な行だけを格納する配列

    ReDim arrAf(1 To 11, 1 To 3)
    
Dim i As Long, j As Long
Dim cnt As Long

    cnt = 1
    
    For i = 1 To UBound(arrBf, 1)
    
        If arrBf(i, 2) <> "不要" Then  'B列が「不要」でない行だけ処理する
        
            For j = 1 To UBound(arrBf, 2)
            
                arrAf(cnt, j) = arrBf(i, j)

            Next j
                
            cnt = cnt + 1
        
        End If

    Next i

    wS.Range("E1").Resize(cnt, UBound(arrAf, 2)) = arrAf
    

End Sub

解説① 二次元配列の準備

まず、元のデータをすべて取り込むarrBfと、そのarrBfから必要なデータだけを転記するarrAfを用意した(arr_Beforeとarr_Afterの略)。

Dim arrBf() As Variant
    ReDim arrBf(11, 3)
    arrBf = wS.Range("A1:C11").Value
    
Dim arrAf() As Variant
    ReDim arrAf(1 To 11, 1 To 3)

 

このとき注意したいのがそれぞれの配列のインデックスの範囲。
arrBfはRangeオブジェクトでセルの値をまるまる入れるのでインデックスは自動的に1から始まる。
しかしarrAfはRedimでサイズだけ指定した空の配列なのでインデックスは指定しなければ0から始まる。
このインデックスのずれが後々ややこしくなるため、arrAfの方はわざわざarrAf(1 To 11, 1 To 3)と
インデックスが1から始まるように指定した。

 

f:id:yohsuke517:20220213220653p:plain

全データを入れる配列arrBfと空の配列arrAf

 

 

解説② 必要な行を転記する

次にarrBfの2列目の要素を上から順に確認し、「不要」となっていない列をarrAfに移していくコードを書いた。

Dim i As Long, j As Long
Dim cnt As Long

    cnt = 1
    
    For i = 1 To UBound(arrBf, 1)
    
        If arrBf(i, 2) <> "不要" Then
           
            For j = 1 To UBound(arrBf, 2)  
            
                arrAf(cnt, j) = arrBf(i, j) 

            Next j
                
            cnt = cnt + 1
        
        End If

    Next i

f:id:yohsuke517:20220213220802p:plain

arrBf( i , 2 )の中が「不要」になっていない行を探す


iはarrBfのすべての行を確認するため、1から11までのイテレータ。
jは「不要」でない行の列を左から数えるため、1から3までのイテレータ。

最後にcntはarrAfにコピーした行をカウントするための変数。

arrBf側の不要行のデータを消すだけではなく行ごと削除するため、上に詰める必要がある。
そのためにcntをarrAf側の行番号として使用することにした。

iを回しながらコピーする行を確認し、arrBf( i , j )の値をarrAf( cnt, j )に移していく作業を繰り返す。

 

f:id:yohsuke517:20220213220931p:plain

arrBf( i , 2)が「不要」でない行をarrAf( cnt , j )に値をコピーしていく

 

 

解説③ その他

jを使わなくても

arrAf( cnt , 1 ) = arrBf( i , 1 )
arrAf( cnt , 2 ) = arrBf( i , 2 )
arrAf( cnt , 3 ) = arrBf( i , 3 )

と書いても同じことだけど、元のデータの列が10や20になると記述が大変なので今回はループで書いてみた。

そして最後に必要なデータだけをコピーしたarrAfをE1セルを左上とした範囲に貼り付ける。

 

   wS.Range("E1").Resize(cnt, UBound(arrAf, 2)) = arrAf

 

この時、もともとarrAfは11行×3列の二次元配列だけど、8行目以降は何のデータも入っておらず不要なので、リサイズする行は cnt を使用する。

f:id:yohsuke517:20220213223056g:plain

実際に実行してみたところ。

 

これでいちおう、二次元配列の行の削除ができた。
厳密にいうと配列の行は一切削除していないけど、必要行だけ取り出すという当初の目的は達成できたので良しとしよう。

 

個人的名著

 

 

google-site-verification=W_k9LyKMYLp-1eq4cMMKOeqJnQ5a8pp4D2UIvuCGVBQ