
マクロ 95回
Dictionaryは vba を高速化したい
場合にかなり威力を発揮します。
そして具体的なメリットとしては辞書の
検索と存在の確認が高速にできるということです。
その結果 vba 全体が高速に動くというわけです。
この記事では、以下の内容について解説しています。
(練習ファイルがダウンロードできます。 練習ファイルの配布)マクロコードは記述されていません。
前回はdictionary を使うために2つの方法があることを説明しました。
一つは CreateObject関数を使う方法と、もう一つは参照設定で
Microsoft scripting runtimeを参照するようにチェックを
入れる方法です。
Create Objectを使う場合は特に
に何の準備もせずに Dictionaryを使うことができます。
だったらこれが一番いいんじゃないかと思うかもしれませんが 一つ欠点があります。
というのはIntellisenseが働かないこと です。
とくにまだDictioanryに慣れていない方は、ぜひMicrosoft の scripting runtime を参照設定してください。

シートの A1:B7までの範囲にわざと重複を作成しています。 これをその下に重複のないデータとして書き出すというコードを考えていきましょう。

前回のコードは重複がない前提で、コードを書いて for 文で回していました。重複がある場合はfor 文の部分を変更していきます。

どのように変更したかと言うと 、for 文の中で if 文を使ってExistsメソッドで存在確認をしたわけです。
If dic.Exists(data(i, 1)) = False Then
dic.Add data(i, 1), data(i, 2) 'Dictionaryに登録
End If

If dic.Exists(data(i, 1)) = False Then
のコードの部分ではdic.Exists(data(i, 1))でディクショナリに存在するか、=Falseでで存在しないならばという意味になります。
false ならば辞書に追加する
dic.Add data(i, 1), data(i, 2)

For i = 1 To UBound(data, 1)
'登録されていない場合
If dic.Exists(data(i, 1)) = False Then
dic.Add data(i, 1), data(i, 2) 'Dictionaryに登録
End If
Next
コードを実行した結果、辞書に重複しないデータを登録できました。そしてその結果を指定したセル範囲に書き出すことができました。

Sub dict02_a()
Dim dic As New Scripting.Dictionary
Dim data, i As Long
data = Range("A2:B7")
For i = 1 To UBound(data, 1)
'登録されていない場合
If dic.Exists(data(i, 1)) = False Then
dic.Add data(i, 1), data(i, 2) 'Dictionaryに登録
End If
Next
Range("A11").Resize(dic.Count) = _
WorksheetFunction.Transpose(dic.Keys)
Range("B11").Resize(dic.Count) = _
WorksheetFunction.Transpose(dic.Items)
End Sub
Excel VBA の Dictionary オブジェクトには、以下のような主なメソッドがあります。
今度は、
キーとアイテムだけでは比較できないケースを考えます。
たとえば、キーにしたい列が2つあるような場合です。そのようなときはどのような工夫が必要でしょうか?
例えば、下記の表のように、列を追加してくだものにA品B品があるとします。個数をキーとしたいのですが、重複を削除できるのでしょうか?

重複しないリストを高速で作るのが目的ですから、辞書にこだわる必要はありません。Keyひとつ、Itemひとつの組み合わせですが、そこを工夫しましょう。
配列で受けとったデータを受け取るリストという空の配列を用意します。
そして重複確認するのは、data(i, 1) & "-" & data(i, 2)というMelonとAをつなげた値です。
dic.に追加するのは、data(i, 1) & "-" & data(i, 2)という連結したデータが無かった時だけです。

わざわざ辞書に追加していますが、その辞書が目的ではないのです。
目的は、そのあとに追加しているリストなのです。
辞書dicは重複を見つけるためにだけ利用しています。
data(i, 1) & "-" & data(i, 2)という形で書き出すのでは、また仕事が増えてしまいますから、
ReDim list(1 To UBound(data, 1), 1 To 3)と、
リストlistを作成したわけです。

重要なコードは以下の部分です。
If dic.Exists(data(i, 1) & "-" & data(i, 2)) = False Then
dic.Add data(i, 1) & "-" & data(i, 2), data(i, 3) '辞書に登録
k = k + 1
list(k, 1) = data(i, 1) 'Key前
list(k, 2) = data(i, 2) 'Key後ろ
list(k, 3) = data(i, 3) 'Item
End If

listは、辞書を間に挟むことで元のdataから重複を削除した配列になっています。
dic.Exists(data(i, 1) & "-" & data(i, 2))
の部分がdic.になければそれをdic.のKeyに追加して、 data(i, 3)はItemに入れます。
さらにlistにも追加する。
list(k, 1) = data(i, 1) 'Key前
list(k, 2) = data(i, 2) 'Key後ろ
list(k, 3) = data(i, 3) 'Item
重複を探す時に、辞書はキーを下図のような形でチェックしています。

シートに書き出すコードは、範囲をしっかり指定してやる必要があります。 このコードではRange("A12)セルを、.Resize(dic.count, 3) でリストのサイズを取得しています。

コードを実行すると、重複が削除されて、listという配列が取得できました。さらにその中身を確認するために、シートRange("A12)から書き出しています。

連想配列Dictionary はキーが一つ、アイテムが一つの、 一対一の組み合わせだから、便利そうだけど意外と使い道がないと思われている方も多いと思います。 ですが実際には配列と組み合わせることで、このように便利に使うことができます。