宛先重複の「地獄」から脱却せよ:Dictionaryオブジェクトで実現するOutlook自動化の要諦
業務自動化を志すエンジニア諸君。君たちの書くVBAは、単に「動く」だけの代物になっていないか?
特に、Excelリストから宛先を抽出し、Outlookでメールを量産する処理。ここで多くの初心者が陥る罠が「宛先の重複」だ。宛先リストに同じメールアドレスが複数回登場し、送信先が汚れる。あるいは、重複チェックのロジックを雑に書いたせいで、ループ処理が指数関数的に遅延する……そんな惨状を目にしてきた。
本稿では、プロフェッショナルな現場で通用する、`Scripting.Dictionary` を用いた「宛先一意化」の最適解を伝授する。
—
1. なぜ「力技のループ」ではいけないのか
初心者はよく、宛先リストの配列を二重ループで回して比較するコードを書く。これは計算量が $O(N^2)$ になる最悪の設計だ。リストが1,000件を超えた瞬間、PCはフリーズし、業務は停止する。
我々が採用すべきは、ハッシュテーブル(連想配列)を用いたアプローチだ。`Dictionary`オブジェクトを使えば、キーの存在確認はほぼ瞬時に終わる。計算量は $O(N)$。規模が拡大しても破綻しない、堅牢なアーキテクチャこそがエンジニアの矜持である。
—
2. 実装:プロフェッショナルな宛先整理ロジック
以下のコードは、単に重複を弾くだけではない。エラーハンドリングと、メモリ管理を意識したプロダクション品質の実装だ。
Option Explicit
‘ 参照設定: Microsoft Scripting Runtime を有効にすること
‘ なぜ参照設定か?:Late Binding(CreateObject)よりEarly Bindingの方が
‘ コンパイル時にエラーを検知でき、IntelliSenseが効くため保守性が圧倒的に高い。
Public Sub SendUniqueEmails()
Dim dict As Scripting.Dictionary
Set dict = New Scripting.Dictionary
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(“AddressList”)
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim i As Long
Dim email As String
‘ 1. Dictionaryで一意の宛先のみを抽出
For i = 2 To lastRow
email = Trim(ws.Cells(i, 1).Value)
‘ 不正なアドレスや空行を弾くバリデーション
If email Like “@.” Then
If Not dict.Exists(email) Then
dict.Add email, Nothing
End If
End If
Next i
‘ 2. 抽出された宛先でメール生成
Call CreateMailFromList(dict)
‘ メモリ解放の習慣化
Set dict = Nothing
End Sub
Private Sub CreateMailFromList(dict As Scripting.Dictionary)
Dim outApp As Object
Dim outMail As Object
Dim key As Variant
Set outApp = CreateObject(“Outlook.Application”)
‘ Dictionaryのキーを連結して宛先を作る(セミコロン区切り)
Dim recipients As String
For Each key In dict.Keys
recipients = recipients & key & “;”
Next key
‘ メール生成
Set outMail = outApp.CreateItem(0)
With outMail
.To = Left(recipients, Len(recipients) – 1)
.Subject = “【重要】業務自動化テスト”
.Body = “宛先整理完了のお知らせ。”
.Display ‘ 送信前に確認を挟むのがプロの慎重さ
End With
Set outMail = Nothing
Set outApp = Nothing
End Sub
—
3. 現場で生き残るための設計思想
① 参照設定(Early Binding)の徹底
`CreateObject` は便利だが、プロの現場では `Microsoft Scripting Runtime` を参照設定することを強く推奨する。型定義(`Dim dict As Scripting.Dictionary`)を明確にすることで、開発効率と実行速度を最大化するのだ。
② バリデーションは「入口」で行う
データソース(Excel)が汚れていることを前提とせよ。`Like “@.”` といった簡単なパターンマッチングをループ内に入れるだけで、未知のゴミデータによる後続処理のクラッシュを未然に防げる。
③ インターフェースとロジックの分離
コード例を見て気づいただろうか? 宛先抽出のロジックと、Outlookを操作するロジックを別プロシージャに分けている。もし将来、メール送信先が「Teams通知」や「Slack API」に変わっても、宛先抽出ロジックはそのまま流用できる。これが「保守性の高いコード」の正体だ。
—
最後に:自動化の先にあるもの
君たちが書くVBAは、単なるスクリプトではない。会社の業務プロセスを定義し、ミスを根絶する「システム」だ。
`Dictionary` を使いこなすことは、データ構造の基本を理解しているという証明になる。まずはこのロジックを手に馴染ませ、次なる課題――例えば、ExcelのデータをJSONに変換してWeb APIを叩くような――高いレベルの自動化へ挑戦してほしい。
技術の深淵は常に君たちの足元にある。さあ、次はどんな非効率を駆逐する?
