VBA データを最新に更新したい（転記）

### 初めに
今回の記事はVBA勉強会にて、お題に沿って自分なりにVBAを書いたものになります。
私自身VBA歴はまだまだ浅く、勉強中の身ですのでベストプラクティスとは言えないコードになっていることと思います。そのためあまり参考にならない可能性がありますのでご了承ください。

### お題の内容
>「社員情報の更新」
社員情報をエクセルで管理し、新入社員や中途社員といった新規社員の追加や、所属支店、氏名の変更が発生したケースについてです。
手動での変更はミスの温床となってしまいます。
そこで、VBAを使用して自動での情報更新を行なってみましょう！
今回も、記述方法、トリガー条件は自由です。
配布ファイルは
「旧社員マスタ」
「変更社員情報」
の2つです。
「変更社員情報」には変更のある社員情報が記載されています。これを元に「旧社員マスタ」の情報を更新してください。
「変更社員情報」は全ての項目が変更されているわけではありません。所属のみ変更されている場合や、氏名のみの変更もあります。

### ファイルの内容
下記画像の2つのファイルを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/d454167c-8129-7d97-5d38-12f7016c4e92.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/c6283bfb-a118-c197-cc65-806c771468f8.png)

### コードと実行結果

```
Option Explicit
Sub 実行()
Application.ScreenUpdating = False

Dim FilePath As String: FilePath = ThisWorkbook.Path
Workbooks.Open FilePath & "\" & "変更社員情報.xlsx", ReadOnly:=True
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim ws As Worksheet: Set ws = wb.Sheets("社員マスタ")
Dim i, j As Long '※←ミスコード! i がVariant型になる。
Dim target As Range '検索で使用
Dim Arr As Variant: Arr = ws.Range("A1").CurrentRegion   '更新データを配列に格納
wb.Close

'新しいデータの数だけ繰り返し
For i = 2 To UBound(Arr, 1)
    Set target = Cells.Find(what:=Arr(i, 1))           '変更する社員IDをターゲットに格納
    If target Is Nothing Then                          '変更するセルが見つからなかったら、最終行をターゲットにセット
        Set target = Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
    End If
    '更新する列の数だけ繰り返し
    For j = 1 To UBound(Arr, 2)
        target.Offset(0, j - 1) = Arr(i, j)
    Next
Next
'罫線を引く
Range("A1").CurrentRegion.Borders.LineStyle = xlContinuous
Application.ScreenUpdating = True

End Sub

```

#### Findメソッドで社員番号が見つかった場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/b32b7cf3-32be-c054-8c7e-c9d901c03d36.png)
下記のコードで旧社員番号を検索します。
```
Set target = Cells.Find(what:=Arr(i, 1))           '変更する社員IDをターゲットに格納
```
見つかった場合、見つかった場所を起点に転記処理を行っていきます。
（変更点以外の転記は無駄ですが、いったんそのままスルーしてください）
```
'更新する列の数だけ繰り返し
For j = 1 To UBound(Arr, 2)
    target.Offset(0, j - 1) = Arr(i, j)
Next
```
#### 見つからなかった場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/4ecbab83-2c11-014b-ee13-84f0940ba80f.png)

検索して見つからなかった場合(RangeオブジェクトのtargetがNothingだったら）
下記のコードで、１列目の最終行から一つ下の行をターゲットとして処理を行っていきます。

先ほど変更点以外上書きしても無駄だといいましたが、こうすることで追加の場合と上書きの場合で処理を共通化できて、コードがすっきりするかなと思った次第です。
```
'見つからない場合、最終行をターゲットにセット
If target Is Nothing Then
    Set target = Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
End If

'更新する列の数だけ繰り返し
For j = 1 To UBound(Arr, 2)
    target.Offset(0, j - 1) = Arr(i, j)
Next
```
#### 実行結果
変更情報の値に更新され、項目も追加されていることがわかるかと思います。
![社員情報変更.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/8141b9b2-d4c9-de3e-212d-164b0f53b0fb.gif)

### 感想
今回、とにかくコードが短くできないかなと考えて、更新情報の上書きと追加の処理を共通化してみたという感じです。（成果の発表の際長い間しゃべりたくないので、できるだけコードを短くして説明をできるだけ短くしたいという魂胆ｗ）
今回学びもあり実りのある勉強会だったと思います。
あと初めてgif作ってみたけど楽しい。。。(笑)

### 学んだこと

変数はまとめて宣言できる認識だったが、今回型を明示的に指定しておらず想定とは違う型になっていた。※勉強会にてご指摘いただきました。
```
Dim i, j As Long '※←ミスコード! i がVariant型になる。
```

Arrの宣言時、Arr()としていないのでRangeオブジェクトが配列に格納されており、そのため配列のインデックス番号が１から始まっていた。配列は配列でも格納する情報が異なると、0から始まらないこともあるということが分かった。
```
Dim Arr As Variant: Arr = ws.Range("A1").CurrentRegion   '更新データを配列に格納
```
ローカルウィンドウ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1744893/35445e5f-445d-e660-330d-31decc2880bd.png)

if文の省略形があることを知ることができた。
分岐しない場合は、一行に収めることで、End If を省略できるらしい。ビビりまくった。
```
If target Is Nothing Then
    Set target = Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
End If
```
```
If target Is Nothing Then Set target = Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
```

以上です。