配列問題その2:2次元配列を1次元に変換する
はじめに
前々回の「配列問題その1:如何にして0を省略するか https://note.com/vba_writer_hiro/n/nc596c8469c91 」にハートがまったくつかなくてちょっとがっかりしているhiroです。ですが、NOTEを始めたばかりなので頑張って投稿していきたいと思います。
今回は、タイトルのとおり、2次元配列を1次元配列に変換する問題です。それってなんの需要があるんだ?と思う方もいると思いますが、前回の0省略など、2次元のままで0を省略しようとすると面倒ですが、1次元配列にしてしまってから0を省略すれば、容易に計算できます。
その他、1次元配列に対する計算のfunctionをいろいろ事前に用意しておけば、2次元用のものを用意する必要は基本的にはないのです。
早速モデルを使って考えていきましょう。
ヨコ方向をひとまとまりに考える場合
2次元配列、本来はタテとかヨコという概念はありませんが、もともとのデータをワークシートから読み取ったり、またできた配列をワークシートに書き出したりするので、タテとかヨコのほうがなじみやすいです。
まず、2次元の配列を想定します。タテ方向の数(=行数)はaa、ヨコ方向の数(=列数)をbbとします。
すると、求めたい1次元配列の要素数 cc=aa*bb となります。
今回はヨコ方向をひとまとまりに考える場合ですから、2次元配列から1次元配列への変換イメージは以下のとおりです。タテ5行、ヨコ7列で考えてみましょう。各要素の数字は乱数で適当につくったので意味はないです。
この配列をヨコ方向、つまり1行を一つの連続したまとまりとして考え、つなげた1次元配列に変換したいのです。
最初の2次元配列を arr(a,b) 求める配列をarr2nd(c) とすれば、
arr2nd(c) = arr( f1(c) , f2(c) )
と書けるようになればいいのです。f1 , f2は cを変数とした関数と思ってください。
では次に、わかりやすく、arr のどこの要素が arr2nd の何番目になるか書いてみましょう。
仮に14番の要素を行と列の番号で書くと、14=2×7 つまり単純に a*b で計算することができます。これは 14が 7 の倍数 だからです。先の関数の書き方に入れると
arr2nd( 14 )=arr(2 , 7)
ですね。それぞれの2、7をどう求めるか。そうすると・・・
arr2nd( 14 )=arr(14/7 , 7)
arr2nd( c )=arr( c / bb , bb )
となります。では、18番の要素はどう考えるか。図をみれば、
arr2nd( 18 )=arr(3 , 4)
であることは一目瞭然ですが、数式に表すと
arr2nd( 18 )=arr( 18を7で割った商+1 , 18を7で割った余り)
arr2nd( c )=arr( int(c / bb)+1 , c mod bb )
となります。
以上から、c が bb の倍数かどうかで、数式が変わることがわかります。
具体的なvbaコード
ここから具体的なコードを書いてみましょう。配列 arr を function に代入するイメージです。
Function arr2Dto1D(ByVal arr As Variant)
'arr2Dto1D:2次元の配列を1次元にする。省略なし 配列は整数(long) ヨコ方向をひとまとめに考える
Dim a As Long, b As Long, c As Long
Dim aa As Long, bb As Long, cc As Long
Dim rownum As Long, colnum As Long
Dim arr2nd() As Long
aa = UBound(arr, 1)
bb = UBound(arr, 2)
cc = aa * bb
ReDim arr2nd(1 To cc)
For c = 1 To cc
If c Mod bb = 0 Then
rownum = c / bb
colnum = bb
Else
rownum = Int(c / bb) + 1
colnum = c Mod bb
End If
arr2nd(c) = arr(rownum, colnum)
Next c
arr2Dto1D = arr2nd
End Function
いかがでしょうか。int や mod の解説は特にしませんが、わりに使う関数です。覚えておくと損はないと思います。
今回はヨコ方向、つまり1行を一つの連続として1次元配列に変換しましたが、タテ方向、1列を一つの連続とする場合もあります。ヨコ方向が理解できればタテ方向は難しくありません。もしよかったら作ってみてください。