Dictionaryオブジェクトとは

Dictionaryはとても高速に動作するため大量のデータを使う場合には有用な手段の1つになります。

Dictionaryオブジェクトはキーと値で1セットとなるデータ形式を持ちます。他の言語であれば連想配列やハッシュマップなどの言い方をされます。

キーは値を検索するために使う「しおり」の役割を持ちます。店で食事をするときに食券を買って食事を受けとりますが、このときの食券がキーで、食事が値になります。

同じDictionaryオブジェクトの中でキーと値のセットは複数持つことも可能ですが、同じキーの重複は出来ません。

以下のようなデータ構造になります。

キー
1 111
2 222
3 333
“1” “111”
“2” “222”
“a” “aaaaa”
“b” “bbbb”
“c” “ccc”

事前準備

Dictionaryオブジェクトを利用するには2通りの方法があります。1つはCreateObject関数を使う方法で、もう1つは参照設定を行う方法です。どちらでも動作は変わりませんが、参照設定を行うと多少高速なこととメソッドやプロパティが入力候補で表示されます。

個人的には参照設定で利用することをお勧めします。

CreateObject関数を使う方法

書き方は以下のようになります。

参照設定を行う方法

VBA画面のツールメニュー→参照設定を選び、参照設定ダイアログで「Microsoft Scripting Runtime」にチェックを付けます。

コードの書き方は以下のようになります。

なお、1行で書いても構いません。

厳密には1行で書かない方がいいのですが、普通に利用する分では問題は発生しませんので1行でも構いません。私自身も1行で書くことが多いです。

このことについての詳細は「VBAでクラス変数の宣言とNewを1行で書いてよいか」をご参照ください。

メソッドとプロパティ

Dictionaryオブジェクトには以下のメソッドとプロパティがあります。

それぞれの引数の「key」はキー、「Item」は値を指します。

メソッド

Sub Add(Key, Item) 新しいキーと値のセットを追加します。

既に追加されているキーの上書きはエラーになります。

Function Exists(Key) As Boolean 指定したキーが含まれているか確認します。

含まれていればTrue、そうでなければFalseを返します。

Function Items() Dictionaryオブジェクトに含まれる全ての値を配列で返します。

Dictionaryオブジェクトはキーの重複は認められませんが、値の重複はありえるため、Itemsメソッドで取得した配列内で値が重複していることはありえますしエラーにもなりません。

Function Keys() Dictionaryオブジェクトに含まれる全てのキーを配列で返します。
Sub Remove(Key) 指定したキーの要素を削除します。
Sub RemoveAll() Dictionaryオブジェクトに含まれる全ての要素を削除します。

プロパティ

Property CompareMode As CompareMethod キーのあいまい検索を許可するかどうかをCompareMethod列挙型の定数で指定します。

Itemプロパティなどで使うキーがDictionaryオブジェクトに格納されているキーと厳密に同じでなければならない場合はBinaryCompareを指定し、大文字・小文字、ひらがな・カタカナ、全角・半角の区別せずに行う場合はTextCompareを指定します。

あくまでも検索する際のキーの条件のため、テキスト比較を設定してもキーの重複が許されるわけではありません。

Dictionaryオブジェクトにデータがセットされている状態ではエラーになります。

CompareMethod列挙型

定数 内容
BinaryCompare 0 バイナリ比較(大文字・小文字、ひらがな・カタカナ、全角・半角を区別します)
TextCompare 1 テキスト比較(大文字・小文字、ひらがな・カタカナ、全角・半角を区別しません)
Property Count As Long キーと値のセットの数を返します。
Property Item(Key) 指定したキーに対応する値の設定および取得を行います。
Property Key(Key) キーを別のキーに変更します。

たとえばキー”1″を”2″に変更したい場合は「Dictionary.Key(“1”) = “2”」のように記述します。

一般的な使い方のサンプル

Dictionaryクラスの全てのメソッドとプロパティを使ったサンプルです。

Dictionaryクラスの各メソッドやプロパティには書き方が分かりにくいものもありますので、不明なものはコードをご参考ください。

配列よりDictionaryクラスを使う利点

Dictionaryクラスとよく比較対象とされるのが配列です。Dictionaryと配列との違いは、キーがあるかないかですが、それによりデータの格納方式にも違いがあります。配列は先頭から順にデータが並んだ状態でデータを格納しますが、Dictionaryにはデータの並び順は持っておらず、あくまでもキーが基準になります。そのため、並び順が重要な意味を持つ場合には後述するキーのソートを行うか、もしくは、Dictionaryクラスよりも配列を利用した方がよい場合があります。

また、配列はデータを格納するための領域をRedim構文やRedim Preserve構文で確保する必要がありますが、Dictionaryにはその必要がありません。Addメソッドを実行すると領域が拡張されてキーと値が追加されます。

Dictionaryは配列と違ってキーでデータを特定できるため、高速にデータの選別が可能であるという特性があります。そのため、多数のデータの中から特定のデータを抽出する、という処理を行う場合は配列よりも圧倒的にDictionaryクラスを使った方が高速に処理できます

もし配列でのデータ抽出を繰り返し行うような処理で遅いと感じることがあるのであれば「VBAで配列を連想配列Dictionaryに変換する」をご参照ください。

キーと値に登録できるデータの種類

Dictionaryを使う際に、キーにとして扱うデータは数値や文字列が多いと思いますが、オブジェクトを指定することが可能で、値にはオブジェクト(例:Range(“A1”))の他にも配列や戻り値を返す関数(例:Timer)などを指定することができます。ただし、キーに配列をセットすることは出来ません。

オブジェクトをキーに設定できるため、以下のように、キーにRangeオブジェクト、値にワークシートや配列をセットすることが出来ます。

また、キーに使う数値と文字列は別物として扱われます。以下のコードにもありますが、1と”1″は重複せずにどちらもキーとして設定されます。

未登録のキーだけを追加したい場合

Dictionaryオブジェクトに未登録のキーだけを追加したい場合は、事前にExistsメソッドで対象のキーが追加済みかどうかを判定した上で、未追加であれば追加するようにします。

以下のような感じになります。

キーの上書きをしたい場合

追加済みのキーに対してAddメソッドを実行するとエラーになります。他の言語のハッシュマップなどではAddメソッドのような追加メソッドで直接上書きが出来るものもありますが、VBAのDictionaryクラスではそれは出来ません。そこでVBAでは疑似的に別の方法で上書きを行うことになります。

上書きの方法には2通りあります。Itemプロパティを使って値の書き換えを行う方法と、Removeメソッドで削除してからAddメソッドで追加する方法です。どちらの方法でも構いませんがItemプロパティでの書き換えの方が手間が少ない分、処理速度も速いです。

以下のコード内でそれぞれの方法を紹介します。

関数の引数でDictionaryクラスを使う場合

Dictionaryオブジェクトを関数の引数としてやり取りを行う場合に、渡した関数側での処理が呼び出し元にも反映されます。

Integer等の数値型やStringの文字列型の場合は関数の引数にByRef指定をしないと呼び出し元にも引き継がれませんが、DictionaryはByRefを付けなくても渡した関数での編集内容が呼び出し元にも反映されます。

以下のサンプルは呼び出し元で1と2を追加し、関数を呼び出してその関数内部で3を追加し、呼び出し元でDictionaryの全てのキーに対応する値を出力しています。

出力すると呼び出し先の関数で追加された3も一緒に出力され、ByRef指定をしなくても関数間で引き継がれていることが分かります。

全キーをソートする方法

Dictionaryクラスのキーはソートの仕組みがありません。ただ、場合によってはキーがソートされていた方が都合がいい場合があります。そのままではソートはできませんので、Keysプロパティで全てのキーを配列として取得して、その全キー配列をソートする方法を行います。

ソートの方法にはいくつかあります。

よく紹介されるソート方法はワークシートの指定セル範囲を並べるSortメソッドを使う方法だと思いますが、個人的にはこの方法は避けた方がよいと思っています。理由は、ソートのためにワークシートやセルを用意しなければならず、使ったあとに削除も必要になりますし、セルへの値の設定や操作は処理が遅いため、せっかくのDictionaryクラスを使っている高速性の利点が犠牲になってしまうためです。

そこで、高速にソートを行うためには2通りの方法があります。1つはソート処理を自分で実装する方法で、もう1つは.NETのArrayListクラスのSortメソッドを利用する方法です。どちらを使ってもいいのですが、.NETの利用はあまりメジャーな方法ではないため、ここではクイックソートを実装する方法を紹介します。

クイックソートについての詳細は「VBAの配列をクイックソートで並べ替え」ご参照ください。ここでは利用しませんがArrayListクラスのSortメソッドについては「VBAの配列を.NETのArrayListのSortで並べ替え」をご参照ください。

コードで使っているクイックソートの関数は上記リンク先のコードをそのまま使っています。

実行すると、クイックソート後は”8″ “9” “10”の順になります。