マトリックスからリストへの変換

### 初めに
VBA勉強会のお題を自分がどのように実装したのかをまとめたいと思います。
今回ググらずに自分の考えで実装したので効率面などはそこまで考慮できておりません。
ご了承ください。

### 課題内容
マトリックス表をリストに変換する。
目的はマトリックスでは見えない部分を、グラフなどで可視化するため。
要件として、項目（プログラミング言語）は今後増える可能性を考慮して拡張性を持たせておいてください。※社員情報の３列は増えないものとする

:::note warn
※使用するデータはオリジナルで作成したもので、ダミーデータとなります。
:::

こちらマトリックス表
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/a56afdd3-1674-ed9a-0ed0-187238218f6f.png)
こちら出力イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/c8d0a510-b918-9216-a2e5-01209f6fd2f5.png)

### コード内容

```vb

Option Explicit
Option Base 1

Sub main()

Application.ScreenUpdating = False

'シート
Dim wsMatrix As Worksheet
Dim wsOutput As Worksheet
Set wsMatrix = ThisWorkbook.Sheets("マトリックス表")
Set wsOutput = ThisWorkbook.Sheets("出力先")

'行列格納用
Dim lastRow As Long: lastRow = wsMatrix.Cells(Rows.Count, 1).End(xlUp).Row
Dim lastcol As Long: lastcol = wsMatrix.Cells(1, Columns.Count).End(xlToLeft).Column

'項目数格納用
Dim n As Long: n = lastcol - 3

'配列
Dim arr() As Variant                '主となるデータ
Dim arrSyainInfo() As Variant       '社員情報（社員ID、部署、名前）
Dim arrLabel() As Variant           '項目ラベル（Python,VBA,Java..）
Dim arrScore() As Variant           '各項目ごとの点数

'カウント
Dim i As Long
Dim j As Long

wsMatrix.Select
'データを配列に格納
arr = wsMatrix.Range(Cells(2, 1), Cells(lastRow, lastcol))
'項目名を配列に格納
arrLabel = wsMatrix.Range(Cells(1, 4), Cells(1, lastcol))

wsOutput.Select

For i = 1 To UBound(arr)
    '最終行を取得
    lastRow = wsOutput.Cells(Rows.Count, 1).End(xlUp).Row
    '社員情報を格納＆転記
    arrSyainInfo = WorksheetFunction.Index(arr, i, Array(1, 2, 3))
    wsOutput.Range(Cells(lastRow + 1, 1), Cells(lastRow + n, 3)) = arrSyainInfo
    '項目名を転記
    wsOutput.Range(Cells(lastRow + 1, 4), Cells(lastRow + n, 4)) = WorksheetFunction.Transpose(arrLabel)
    '点数格納用の配列を初期化
    ReDim arrScore(1, n)
    '配列から点数部分のみを抽出して配列に格納
    For j = 1 To n
        arrScore(1, j) = arr(i, j + 3)
    Next
    '点数を転記
    wsOutput.Range(Cells(lastRow + 1, 5), Cells(lastRow + n, 5)) = WorksheetFunction.Transpose(arrScore)
Next

End Sub

```

### 解説
まず初めに最終行と最終列を取得し、マトリックス表をすべて配列Arrに格納します

```vb: 最終行列を取得
'行列格納用
Dim lastRow As Long: lastRow = wsMatrix.Cells(Rows.Count, 1).End(xlUp).Row
Dim lastcol As Long: lastcol = wsMatrix.Cells(1, Columns.Count).End(xlToLeft).Column
```
```vb: 見出しを含めずにすべての要素を配列に格納
'データを配列に格納
arr = wsMatrix.Range(Cells(2, 1), Cells(lastRow, lastcol))
```

要件にあった今後項目が増える可能性を考慮する点については、最終列数からー３したもの（社員情報を抜いた数）を変数に格納し、使用していきます。
項目名についても配列に格納します。
項目名格納時は、１行目の見出しの４列目以降すべてを格納します。

```vb
'項目数を格納
Dim n As Long: n = lastcol - 3
```
```vb
'項目名を配列に格納
arrLabel = wsMatrix.Range(Cells(1, 4), Cells(1, lastcol))
```

For文による繰り返し
```vb
For i = 1 To UBound(arr)
    '最終行を取得
    lastRow = wsOutput.Cells(Rows.Count, 1).End(xlUp).Row
    '社員情報を格納＆転記
    arrSyainInfo = WorksheetFunction.Index(arr, i, Array(1, 2, 3))
    wsOutput.Range(Cells(lastRow + 1, 1), Cells(lastRow + n, 3)) = arrSyainInfo
    '項目名を転記
    wsOutput.Range(Cells(lastRow + 1, 4), Cells(lastRow + n, 4)) = WorksheetFunction.Transpose(arrLabel)
    '点数格納用の配列を初期化
    ReDim arrScore(1, n)
    '配列から点数部分のみを抽出して配列に格納
    For j = 1 To n
        arrScore(1, j) = arr(i, j + 3)
    Next
    '点数を転記
    wsOutput.Range(Cells(lastRow + 1, 5), Cells(lastRow + n, 5)) = WorksheetFunction.Transpose(arrScore)
Next
```
転記先へ転記する際は、１つの項目が転記終わったら、最終行を新たに取得して、その最終行についかしていくという流れで、今回一括代入ではありません。（拙いコードで申し訳ありません）

```vb: 社員情報の転記
'社員情報を格納＆転記
    arrSyainInfo = WorksheetFunction.Index(arr, i, Array(1, 2, 3))
    wsOutput.Range(Cells(lastRow + 1, 1), Cells(lastRow + n, 3)) = arrSyainInfo
```
WorksheetFunction.Indexは、指定した配列のインデックスを指定するとその要素を取得できるものになります。
社員情報の欄は、配列の(i, 1)(i, 2)(i, 3)と固定されているため、Array（1, 2, 3）で取得します。

配列Arrの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/83a12d2b-04ea-4644-a5d1-7cfe361af3a6.png)

切り出し後arrSyainInfoの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/584a63e2-f9c9-16f6-71da-7271227a0e3e.png)

社員情報を転記する際は、行をn（プログラミング言語の要素数）の分だけ拡張して転記します。

出力はこんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/4258bc31-a2e7-bb1d-e448-f2fd81669ba5.png)

次に項目名の転記ですが、転記する際に配列の転置（行列の入れ替え）を行う必要があります。

```vb: 配列を転置して転記
'項目名を転記
wsOutput.Range(Cells(lastRow + 1, 4), Cells(lastRow + n, 4)) = WorksheetFunction.Transpose(arrLabel)
```
arrLabelの配列はこのようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/9d37401d-a8d7-cdf2-b56c-893e77871037.png)

転置しない場合の出力はこうなります。
思った動きと違います。arrLabel(1)が範囲拡張されて転記されているような形になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/ce7f70be-45eb-6c49-686a-519c93744a31.png)

転置した場合の出力はこちらになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/ac2c6e24-2b2f-d74e-77e7-df90521617c0.png)

次に点数部分の転記です、
点数の配列は、Worksheet.Function.Indexを使用できません。
理由は要素数が増える可能性があるため、インデックスを指定できないからになります。

```vb: 点数の配列の取り出し
'社員情報は要素数が固定されているので1, 2, 3と指定できるが
arrSyainInfo = WorksheetFunction.Index(arr, i, Array(1, 2, 3))
'【例】同じように取得はできるが、要素増えたら対応できない（コードを修正、9を追加する必要が出てくる）
arrScore = WorksheetFunction.Index(arr, i, Array(4, 5, 6, 7, 8))
```
そのため違うやり方で点数を取得していきます。
具体的には、Forを利用し、新しい配列に値を代入していく流れになります。

arrScoreを繰り返し毎に初期化して使いまわしています。
項目数（n）の数だけ領域を確保します。

```vb: ループごとに配列を初期化
'点数格納用の配列を初期化
ReDim arrScore(1, n)
```

カウンタｊを使用し、nの数だけarrから要素を取り出していき、arrScoreに代入します。
```vb: 
'配列から点数部分のみを抽出して配列に格納
For j = 1 To n
    arrScore(1, j) = arr(i, j + 3)
Next
```
作成したarrScoreの中身はこちらになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/f1e52a1e-ab0d-b597-9922-e113fb4b891e.png)

こちらのまた２次元方向に値が格納されているので、配列を転置し、転記します。

```vb: 転置＆転記
'点数を転記
wsOutput.Range(Cells(lastRow + 1, 5), Cells(lastRow + n, 5)) = WorksheetFunction.Transpose(arrScore)
```
出力はこうなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/3e2a2881-5253-9ae8-649a-a920978ba9e1.png)

以降マトリックスの行数の数繰り返すということになります。

### 終わりに
5000行ほどの転記で、速度的には1秒かからないのでパフォーマンスに関しては現状問題ないですが、何倍も増えてくると、一括転記も検討した方がよいかもしれませんね。
常に100点満点のマクロを作ろうとしなくてもいいかという私の考えが表れています(笑)

### 最後に
せっかくなので整形したデータを活用して可視化してみます。
PowerBIを使用します。
部署ごとに言語平均を出してみましたーというものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/0b02b723-f9a9-b741-60a5-6faf6369ef12.png)

データソースの中身は先ほど作成したリスト形式のデータベースです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/c585c8a2-f3af-0735-263a-5d8dee1ccd33.png)

以上