|
▼まりな さん:
>VBAが使えない私は非常に複雑で解読不能でした。。
>Dictionaryオブジェクトで重複チェック
について、少し踏み込んで コードの解説してみます。
シートのB列に 以下のようなデータがあるとします。
[B]
[1] key
[2] ●
[3] ▲
[4] ●
[5] ■
[6] ●
[7] ▲
B列の2行目から、なのでプログラムの先頭定数宣言を
Const COL1 = 2 '検索対象列
Const ROW1 = 2 '検索する最初の行
と宣言します。
さて、
> '↓検索範囲データを(文字列にして)配列にコピーする
> With Columns(COL1).Cells
> v = Application.Text(Excel.Range _
> (.Item(ROW1), .Item(.Count).End(xlUp)), "@")
> End With
を実行すると、
配列v は以下のようにB列の処理対象範囲のデータが格納されます。
※シートの2行目からですが、配列の1番目の要素は[1]です。
v(1,1) = "●"
v(2,1) = "▲"
v(3,1) = "●"
v(4,1) = "■"
v(5,1) = "●"
v(6,1) = "▲"
さて、その次に Dictionaryオブジェクトを宣言し、
> '↓Dicionaryを使って重複チェック
> Set dic = CreateObject("Scripting.Dictionary")
この配列(縦ベクトル)を上から順に格納していきます。
Dictioanryとは いってみれば バケツの集合のような
ものです。ただこのバケツにはすべてラベルが付いていて、
各バケツに付けられたラベルは他のバケツと重複しない
ことがDictionaryバケツの特長です。
それでは、
> ReDim dup(1 To UBound(v), 0)
により、検索配列v と同じ大きさの結果出力用の配列dup を
用意したあとのLoop処理を、いま配列v に格納されている
データ(これをDictionaryでは keyといいます) を順に処理
するLoop過程を↓
> For i = 1 To UBound(v)
> If dic.Exists(v(i, 1)) Then 'すでにこのキーが辞書にあれば
> n = dic(v(i, 1)) 'このキーがどの行で出現したかを得る
> If n > 0 Then
> dup(n, 0) = "重複" '直前の出現行に「重複」書き込み
> dic(v(i, 1)) = 0
> End If
> dup(i, 0) = "重複" 'この行に「重複」書き込み
> Else
> dic(v(i, 1)) = i '行のデータを出現行とともに辞書に入れる
> End If
> Next
実際にやってみましょう。
-------------------
i = 1 のとき v(i,1) は "●"です。
> If dic.Exists(v(i, 1)) Then 'すでにこのキーが辞書にあれば
のIf文では、「"●" というラベルのバケツがDictionaryにすでに
存在していたら...」という判断をしています。
最初どんなラベルのバケツも存在しないので、
> Else
> dic(v(i, 1)) = i '行のデータを出現行とともに辞書に入れる
へジャンプします。
いま i=1 なので、上の文は具体的には
> dic("●") = 1 '行のデータを出現行とともに辞書に入れる
という意味になります。つまり、
「Dictionaryに "●"というラベルの付いたバケツを用意し、
そのバケツの中に 数値の1を入れる」
という意味になります。
-------------------
i = 2のときも、同様です。
"▲"というラベルのバケツはまだ存在しませんから、
> dic(v(i, 1)) = i '行のデータを出現行とともに辞書に入れる
へジャンプして、
dic("▲") = 2
により、「"▲"というラベルのバケツを用意し、その中に数値2 を入れる」
処理をします。
-------------------
i = 3 v(3,1)="●"のときには、これまでとちがう流れになります:
> If dic.Exists(v(i, 1)) Then 'すでにこのキーが辞書にあれば
の判断で 「"●"というラベルのバケツはすでに存在する」となりますので
プログラムの実行は If文のすぐ下に↓ 移り、
> n = dic(v(i, 1)) 'このキーがどの行で出現したかを得る
で、 n = dic("●") つまり、
「"●"という名のバケツの中に入っている内容物」を n に取り出し
ています。「"●"という名のバケツの中」には i = 1 のときに 1 が
放りこまれていましたね(^^
たから ここで n は 1 となります。
> If n > 0 Then
> dup(n, 0) = "重複" '直前の出現行に「重複」書き込み
> dic(v(i, 1)) = 0
> End If
のIf n > 0 だったときの処理は、
結果の配列dupの n行目(つまり1行目)に "重複"と書き込み、
さらに、"●"というラベルのバケツ内を 0 にする
という処理をしています。0 を入れるというのは バケツを空にする
ということです。つまり"●"というラベルのバケツは存在し続けるけど、
中身が「空」になる、ということです。
で、直後の処理↓で
> dup(i, 0) = "重複" 'この行に「重複」書き込み
結果の配列dup 内の現在の行(3)にも "重複"を書き入れます。
i = 1 〜 3 までの処理で 配列の内容と Dictionary内のバケツは
このようになっています:
v(1,1) = "●" dup(1,0) = "重複"
v(2,1) = "▲" dup(2,0) = Empty
v(3,1) = "●" dup(3,0) = "重複"
dic("●") = 0
dic("▲") = 2
-------------------
さて、この調子でつづけると、
i = 4, v(i,1) = "■" のときそういうラベルのバケツはまだ
存在しませんから dic("■") = 4 の処理が実行され、
"■"というラベルのバケツが用意されそのなかに4が
収まります。
-------------------
i = 5, v(i,1) ="●"のときには、すでにある"●"という名の
バケツの内容物を取り出しますが、中は「0」です。
つまり、直前の"●"が出現した行にはすでに"重複"は書きこ
まれている、ということです。
よって、If文内の処理はせず、ただdupの5番目の要素に
"重複"を書き込む処理だけをします。
-------------------
i = 6, v(6,1) ="▲"のときは、"▲"というラベルのバケツは
すでにあり、その中身を調べる n = dic("▲") と n = 2
が入っているので、dupの2番目に"重複"を書き込み、
処理済みということで バケツ"▲"のなかを 0 にします
dic("▲") = 0
-------------------
このようにして最終的に dupは
● dup(1,0) ="重複"
▲ dup(2,0) ="重複"
● dup(3,0) ="重複"
■ dup(4,0) =Empty
● dup(5,0) ="重複"
▲ dup(6,0) ="重複"
となりますので、
これを 指定の列に貼りつければ ↓
> ' COL2 列に結果を書き出す
> Cells(ROW1, COL2).Resize(UBound(dup)).Value = dup
処理は完了、ということです。
いかがですか?
Dictioanryについて、多少はご理解いただけましたか?
|
|