マクロ講座VBA Dictionary 連想配列の使い方
マクロ 95回
Dictionary を使って重複したデータの中から重複しないリストを作成する
Dictionaryは vba を高速化したい
場合にかなり威力を発揮します。
そして具体的なメリットとしては辞書の
検索と存在の確認が高速にできるということです。
その結果 vba 全体が高速に動くというわけです。
この記事では、以下の内容について解説しています。
- VBAのDictionaryオブジェクトについての説明
- 重複したデータから重複しないリストを作成する方法の解説
- Dictionaryを使った操作方法(キーと値の追加、存在確認など)
- 配列と組み合わせたDictionaryの利用方法
(練習ファイルがダウンロードできます。 練習ファイルの配布)マクロコードは記述されていません。
連想配列を使って重複のないリストを作成する
前回は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 オブジェクトには、以下のような主なメソッドがあります。
- Add
- キーと値のペアを辞書に追加します。
- Remove
- 指定したキーを持つ要素を辞書から削除します。
- Exists
- 指定したキーを持つ要素が辞書に存在するかどうかを調べます。
- Item
- 指定したキーを持つ要素の値を取得します。
- Items
- 辞書内のすべての値を配列で返します。
- Keys
- 辞書内のすべてのキーを配列で返します。
- Count
- 辞書内の要素の数を取得します。
- CompareMode
- 辞書内のキーの比較方法を設定します。
今度は、 キーとアイテムだけでは比較できないケースを考えます。
たとえば、キーにしたい列が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
Dictionary オブジェクトのメソッド
Listは重複を削除した配列になる
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と配列を組み合わせるとさらに便利になる
連想配列Dictionary はキーが一つ、アイテムが一つの、 一対一の組み合わせだから、便利そうだけど意外と使い道がないと思われている方も多いと思います。 ですが実際には配列と組み合わせることで、このように便利に使うことができます。