この人はVBAがどのくらい出来るのかなって思った時にする質問で
簡単に聞けて、簡単に答えられるのは
「Dictionaryって使ってますか?」ではないでしょうか。
使っているか、使ってないか、なんかよくわからない説明をするか
反応によってその人のレベルがよくわかるようです。
私は「知っているけど使っていない」というなんかよくわからない説明をする派でした。
QiitaでこのDictionaryを使っているサンプルがあって、
私のレベルは「詳しく知らないから使っていない」だったことを知り、Dictionaryについて調べてみましたのでまとめてみます。
簡単にDictionaryについて説明
英語のDictionaryを訳すと「辞書」ですが、他のプログラム言語でいうと、連想配列やHashと呼ばれるものです。辞書は忘れていいと思います。
Hashと同様にkeyとvalueを一つのセットとして、複数セット持つことができます。(VBAではvalueではなくitemと言います。ですが、以下valueで統一します)
keyに重複は許されていません。後述しますが、重複されたデータを格納しようとすると、エラーになるか、valueが上書きされます。
このあたりも他の言語のものと同じだと思います。
使い方
宣言
変数のデータ型をvariantかobjectにする。
CreateObject関数を使う。
Dim dic As Variant
Set dic = CreateObject("scripting.dictionary")
配布等を考えるとこのやり方がオススメです。
他の方法は、参照設定をし、dimでnewを使うか、newでインスタンスを作ってください。
私は参照設定方式を使っていませんのでサンプルなしで。
格納
Collectionと違い、keyは数値でも文字列でも大丈夫です。
dic(key) = value
' dic(key)はdic.item(key)の省略版です。
このやり方ならkeyの重複に関しては上書きをしていきます。
上書きが嫌なら dic.exists(key)
がkeyの存在有無をbool型で返してくれるので、これを使って格納処理を制御するのがいいと思います。
dic.Add key, value
このやり方だと、すでにkeyが存在する場合エラーが発生します。
基本的に私はaddメソッドを使っていません。
余談ですが、Collectionのaddとはkeyとvalueの並びが逆のようです。
値を呼び出す
dic(key)
でvalueを返します。
keyが分かっていて、一つだけならこのやり方ですね。
.keysと.itemsで配列を取得
Dim b As Variant
Dim a As Variant
a = dic.keys
b = dic.items
aにはdicのkeyたちが配列になって入ります。
bにはdicのitemたちが配列になって入ります。
forで回す
VBAではfor
とfor each
がありますが、両方とも使えます。
for next
上記の配列を踏まえてforを使います。
Dim i As Long
Dim a As Variant
a = dic.keys
For i = LBound(a) To UBound(a)
Debug.Print a(i), dic(a(i))
Next i
私の場合、このやり方をすると頭が混乱してきます。。
aはkeyたちが入っている配列です。
その配列の要素数を使ってforを回します。
iの数値を使ってaのkeyをひとつずつ取り出し、そのkeyを使って元のdicに入っているvalueを参照します。
for each next
VBAの場合、「keyとvalueのセット」を回すことができません。inの後にdic.keysかdic.itemsを指定します。
Dim b As Variant
For Each b In dic.keys
Debug.Print b, dic(b)
Next b
' bにはkeyが入ります。
' このbを使ってvalueを呼び出すことが可能です。
For Each b In dic.items
Debug.Print b
Next b
' bにはvalueが入ります。
' この場合はkeyを取り出すことはできません。
下記の例ではin dicになっていますが、dicに.keysが補完されるようです。
Dim b As Variant
For Each b In dic
Debug.Print b, dic(b)
Next b
注意点
格納されていないkeyを参照すると、
dic(key) = EMPTY
が生成されます。
dic.countで要素数が返ってきますが、上記のEMPTYが入っているものも要素数の中にカウントされることになります。
forで回したりする場合はご注意ください。
For i = 1 To dic.Count
If dic(i) <> 0 Then
'......
End If
Next i
などとすると予期せぬ要素が追加される可能性があります。
Dictionaryにはインデックスという概念はないようです。
keyに連番を振って、それをforで回すという方法ではなく、
for eachでkeyを取得し、keyを使う方法をオススメします。
サンプル(pivot)
サンプルとして簡単なpivotを作ってみます。データの個数を数えるだけのものです。
activesheetのA列にあるデータがそれぞれいくつあるかを数えます。
Dictionaryの使い方は、keyを個々のデータにします。valueはインクリメントしていき、個数を表すようにします。
Public Sub sample_pivot()
Dim ws As Worksheet
Dim y As Long
Dim dic, a, k
Set dic = CreateObject("scripting.dictionary")
a = ActiveSheet.UsedRange.Value
For y = LBound(a) To UBound(a)
k = a(y, 1)
dic(k) = dic(k) + 1
Next y
y = 0
Set ws = ActiveWorkbook.Worksheets.Add
For Each k In dic
y = y + 1
ws.Cells(y, 1).Value = k
ws.Cells(y, 2).Value = dic(k)
Next k
End Sub
出力の前にkeysを取得し、そのkeyを並び替えてから呼び出せばエクセルのpivotのようになります。
おわりに
説明が下手で理解しづらいかもしれませんが、ぜひDictionaryを活用して、ご自身のマクロの中にpivotを組込んでみたりしてください。
間違っているとこや、誤字がありましたらご指摘ください。
Dictionaryのうまい活用方法とかもぜひ教えてください。