Dictionaryの各メソッド・プロパティには省略記法が存在します。
各省略記法の使い方と注意点について解説します。
Itemプロパティの省略
Dictionaryの各要素を取り出す以下のコードがあったとします。
' DictionaryのKey,Itemをセルに出力する Dim R As Long: R = 2 Dim key商品 For Each key商品 In Dic商品リスト.Keys Cells(R, 1) = key商品 Cells(R, 2) = Dic商品リスト.Item(key商品) R = R + 1 Next
このコードのうち、まず省略して書けるポイントがこちらです。
Cells(R, 2) = Dic商品リスト.Item(key商品) ' ↓ Itemは既定のプロパティなので省略可 Cells(R, 2) = Dic商品リスト(key商品)
おなじみの「オブジェクトやコレクションの.Itemを省略」ですね。
Dictioanryも既定のプロパティは「Item」になっていますので、
Dictioanry(Key名)でItemを持ってくることができます。
実はこの省略記法、普段から無意識によく使っており、
Worksheets.Item("○○") ' ⇩ 省略 Worksheets("○○")
このコードも同じ記法を用いたコードだったりします。
一般的な書き方ですので、問題なく推奨できるコードです。
読みやすさ重視で「.Item」を書くのももちろんありですし、
慣れてきたら基本省略するルールで書いても全く問題ありません。
お好きな書き方を採用しつつ、
他の人のコードを読むために「省略できる」ことは知っておきましょう。
For Each時のKeysメソッドの省略
先ほどのコード(再掲)にはもうひとつ省略できるコードがあります。
' DictionaryのKey,Itemをセルに出力する Dim R As Long: R = 2 Dim key商品 For Each key商品 In Dic商品リスト.Keys Cells(R, 1) = key商品 Cells(R, 2) = Dic商品リスト.Item(key商品) R = R + 1 Next
このコードでは↓のようにKeysを省略することも可能です。
For Each key商品 In Dic商品リスト.Keys ' ↓ KeysはFor Eachで使うときだけ省略可(非推奨) For Each key商品 In Dic商品リスト
と、こちら可能は可能なのですが、
これはわかりにくく危ない省略記法なのでやめておきましょう。
先ほどの「オブジェクトやコレクションの.Itemを省略」は一般的な記法ですが、
今回のKeys省略はDictionary特有の省略記法であり、
普段使っているほとんどのオブジェクトでは使用できません。
For Each ws In Thisworkbook.Worksheets ' ⇩ この省略はほとんどのオブジェクトで不可 For Each ws In Thisworkbook ' ← これはエラー
しかも厳密にいうと、
For Each key商品 In Dic商品リスト.Keys For Each key商品 In Dic商品リスト
この二つのコードは実は全く違う動きをしており、
省略時にKeysメソッドを実行してくれているわけではありません。
「Dic商品リスト」で「Dic商品リスト.Keys」の代わりができるわけではないため、
以下のように配列を使うコードでこの省略をするとエラーになります。
Debug.Print Join(dic商品リスト.Keys, ",") ' ⇩ この省略は「型が一致しません」エラーとなる Debug.Print Join(dic商品リスト, ",") ' JoinはArrayが欲しいのにDictionaryが来たのでエラー
「Dic商品リスト」が配列を返しているわけではなく(よく考えたら当たり前)、
For Each使用時にDictionary特有の動きがあるというだけなんですね。
このように、Keysの省略は内部処理が結構複雑ですし、
そもそも省略したとき「Keys」「Items」どっちになるのかが不明瞭です。
後で読み返すときにいらぬ誤解を招く恐れがありますし、
この省略記法は原則使わないようにしておきましょう。
と言いつつもこの記法は、
「狙ってなくても間違って書いてしまう」ことが良く起きます。
他の人のコードを読んだりデバッグ作業をするのに必要な知識になるので、
「For Each における.Keysの記述は省略できる」
という仕様は知っておいて下さい。
Addメソッドを省略しての直接代入
Dictionaryには知らないとびっくりする、
強力な利便性と危険性を併せ持った省略記法が存在します。
例えば「商品ごとのデータ件数を数える」以下のコードを書いたとします。
If Dic商品リスト.Exists(key商品) = False Then Dic商品リスト.Add key商品, 1 Else Dic商品リスト(key商品) = Dic商品リスト(key商品) + 1 End If
- 「key商品」が初登場であればItemを1にして新規登録
- 「Key商品」が既登録であればItem = Item + 1
という、明瞭で読みやすいコードですね。
実はこのコード、以下のコードに大幅に省略することができます。
Dic商品リスト(key商品) = Dic商品リスト(key商品) + 1
短縮というか、4行目のコードそのままですね。
要するに、
'If Dic商品リスト.Exists(key商品) Then ' Dic商品リスト.Add key商品, 1 'Else Dic商品リスト(key商品) = Dic商品リスト(key商品) + 1 'End If
実はこれでも同じ処理になるということなんですよ。
初めて知ったときはびっくり仰天する仕様です。
DictionaryオブジェクトのItemプロパティは、
「渡されたkeyの登録が無ければその場で作る」
という素晴らしく便利でなかなか危険な仕様を持っています。
要するにExistsとAddも裏でやってくれるということなんですね。
まさに「4行目のコードだけでOK」なのです。
この省略記法を知っていると、ただでさえ便利なDictionaryを、
即席マクロの高速コーディングにも活用できるようになります。
是非とも覚えて活用したいところです。
が、見てわかる通りワンライナーになった分可読性が落ちますし、
この仕様を知らない人が見ると、突如代入が始まって「???」となります。
この仕様のため、
- 代入時に意図しない新規キーの登録を検知できない
- 同じく意図しない既存キーへの上書きも検知できない
- イミディエイトウィンドウやウォッチウィンドウでItemを調べるとそれで新規登録が発動してしまう。
あたりにかなり危険な罠があることも知っておかなければなりません。
これがあるからExists/Addが不要なんてことは全くなく、
既/未登録の処理が複雑なら素直にExistsで分岐した方が読みやすいことが多いです。
強力な武器ですが諸刃の刃的な面もあるので上手く使っていきましょう。
Keys(i)はKeys.Item(i)の省略ではない
最後にひとつ、間違った省略記法の話です。
たまに見かける「Keys(i)」というコードですが、
実はこの書き方はやってはいけません。
DictionaryのKeys/Itemsメソッドは、
すべてのKey/Itemを1次元配列にして返すメソッドです。
つまり、
「引数はなくて、返り値が1次元Arrayのメソッド」
というのが正しい解釈です。
よって「Keys(i)」と書いた場合の「i」の意味合いは、
- DictionaryのKeysメソッドの「引数に渡したi」ではなく
- DictionaryのKeysメソッドが返す「配列の添字に渡したi」
と読み解かなくてはなりません。
「i」は配列さんの持ち物で、Dictionaryさんのものではないということですね。
省略という意味で言うなら、「Keys(i)」という書き方は
Keys()(i) ' ↓ Keysの引数がないため省略可 Keys(i)
この省略を行っているというのが正しい理解になります。
これを理解した上で、以下のコードを見てみてください。
Dim i As Long For i = 0 To Dic商品リスト.Count - 1 Debug.Print Dic商品リスト.Keys(i) Next
たまに見かけるこんなコードですが、
このコードはなんと配列をループの回数だけ作っているコードです。
もし1万回ループした場合は、要素1万の配列を1万個作ります。
KeysはIndexをもつプロパティではなく、全Keyを配列に変換するメソッドです。
この違いはしっかり理解しておきましょう。
詳しくはこちらの記事をどうぞ
www.limecode.jp
以上でDictionaryの各プロパティ・メソッドの省略記法の解説を終わります。
まとめますと、
- Dic商品リスト.Item(key商品) → Dic商品リスト(key商品) はOK
- For Each key商品 In Dic商品リスト「.Keys」は省略NG
- Exists & Add を省略しての即代入は便利だけど気を付けて
- Keys(i)、Items(i)という書き方は間違い(都度配列を作るので激重)
こんな感じでしたね。
Dictionaryは非常に便利なオブジェクトです。
各記法をしっかり理解して、使いこなしていきましょう。