連想配列
連想配列(Dictionary オブジェクト)使ってますか?御存知のとおり、文字列をキーにした配列です。
https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/dictionary-object
(リンク microsoft公式)
えっ重複チェックにしか使ってない?itemにいらない情報しか入れたことがないって?奇遇ですね。半年前までの私と完全に同じです。
ただ、それはあまりにもったいない!Excelならではの連想配列の使い途があるんです。
連想配列の利点
ハッシュ検索を行えることです。特定の単語と一致するものを検索するとき、先頭から順に確認していく線形探索法を使っていては,平均探索回数が(n+1)/2となってしまうところ,連想配列に登録されたkeyにあるかをexistsでチェックすれば,平均探索回数1回で検索できるのです。
https://www.atmarkit.co.jp/ait/articles/0603/31/news116.html
(リンク @IT atmarkit)
なので,重複チェックに使ったりするわけです。
ただ,重複チェックはkeyの情報だけでできるので,itemにはいらない情報を入れることになります。カウンタとか。それだけではあまりにもったいない!
文字列データの修整に,連想配列を使ってみる
スクレイピングしたデータや、社内システムから引いてきたデータの表記変更をするとします。
私は、こういうときExcel上に表を作って表記のユーザー編集を可能にしています。SelectCase文の条件分岐を表にするイメージです。
たとえば,氏名から略称を引くための表を作成したとします。
もし氏名に該当したら略称をメッセージで出すというマクロを書きたいとき,単純に上から順に繰り返し処理で確認するとこのようになります。
Submsg_short_name()DimiAsIntegeri=1DoWhileCells(i,1)<>""IfCells(i,1)=Cells(1,4)ThenMsgBoxCells(i,2)EndIfi=i+1LoopEndSub
ただ、これを繰り返すとどうなるでしょうか。
1回探すなら(n+1)/2
4回探したら(n+1)/2*4
かなり時間がかかってしまいます。
そこで、連想配列の出番です。
Submsg_short_name()DimnameArrayAsObjectSetnameArray=CreateObject("Scripting.Dictionary")DimiAsIntegeri=1DoWhileCells(i,1)<>""IfNotnameArray.Exists(Cells(i,1))ThennameArray.AddCells(i,1).Value,Cells(i,2).ValueEndIfi=i+1LoopIfnameArray.Exists(Cells(1,4).Value)ThenMsgBoxnameArray.Item(Cells(1,4).Value)EndIfEndSub
※cellの値をkeyやitemとして代入したり,existsで確認したりするときは,.valueをつけてセルの値であることを明示する必要があります。
いったん全てのkey(氏名),item(略称)を連想配列に格納したら,その後はnameArray.Exists(文字列)で存在確認でき,nameArray.Item(文字列)でitemを取り出すことができます。
1回探すなら1
4回探したら4
と,表の行数にかかわらない処理速度で要素を取り出せるのです。
もっと複雑な条件分岐に対応する
連想配列自体は二次元配列にすることができません。
ただ,連想配列のKeyから、もっと色々な情報を引きたくなったら、セルの行を外部キーにして、可変二次元配列と関係づけ、データベースのようにつなげることができます。
たとえば,氏名から略称・受賞作の双方を引く必要があったとすると,下の図のように配列をつくります。
Submsg_short_name()DimnameArrayAsObjectSetnameArray=CreateObject("Scripting.Dictionary")DimshortNameArray()AsStringDimiAsIntegeri=1DoWhileCells(i,1)<>""IfNotnameArray.Exists(Cells(i,1))ThennameArray.AddCells(i,1).Value,iReDimPreserveshortNameArray(1,i)shortNameArray(0,i)=Cells(i,2).ValueshortNameArray(1,i)=Cells(i,3).ValueEndIfi=i+1LoopIfnameArray.Exists(Cells(1,4).Value)ThenDimforeignKeyAsIntegerforeignKey=nameArray.Item(Cells(1,4).Value)DimmsgAsStringmsg="セル(1,4)の外部キー:"&foreignKey&vbLfmsg=msg&"略称:"&shortNameArray(0,foreignKey)&vbLfmsg=msg&"代表作:"&shortNameArray(1,foreignKey)MsgBoxmsgEndIfEndSub
※可変二次元配列は、最後の要素数しか可変にできません。
氏名の文字列から、略称・受賞作の両方を引けるようになりました。
連想配列の使いどころ
ずばり、文字列についてそこそこ分岐の多いSelectCase文を使うときです。特定の単語ごとに関連要素を引くとき、と言い換えることもできます。
分岐を増やせば増やすほど遅くなるというときの特効薬になります。セルの読み込みは一回のみですし、分岐のための検索は、平均一回のみで済みますから。
ただ、含む(Instr)という分岐などには使えないので、やはりスクレイピングなどのローデータ処理が向くと思います。
あまりに膨大な連想配列(や可変二次元配列)を作ると、メモリを圧迫してしまう可能性はありますが、文字列処理レベルではまず大丈夫かと思います。