連想配列(Dictionary)③-キー一覧を取得する方法と高速化のテクニック-

スポンサーリンク

キー一覧を入力するのが面倒?

連想配列(Dictionary)を使用するようになって行数の多いデータの加工が楽になったのですが、連想配列を使用する際によく使う「キーの一覧を別シートに出力する方法」がないか調べてみたのでその手順についてご紹介します。

連想配列のキー一覧を出力するケースとしては、SUMIFS関数の代わりに連想配列を使用した場合などがあります。
キー一覧を別シートに出力して、その右に連想配列に格納されている集計結果を表示させることが多いと思います。

これを知らないとせっかく連想配列を用いていても、別シートを作成する際に元シートのキー部分をコピーして貼り付け後に重複削除をするという面倒な手順を踏むことになります。

サンプルシート

サンプルシートは前回こちらの記事で使ったものと同じものになります。
商品の売り上げ一覧表の中で商品ごとの金額の合計を別シートに出力する用途をイメージしたものです。

前回ご紹介したコードでは、すでに重複のないJANコードの一覧はすでに別シートに出力している状態でVBAを実行していましたが、今回はその重複のないJANコード一覧も出力するフローを想定しています。

vba156シート1

スポンサーリンク

連想配列のキー一覧を出力する方法①-Keysプロパティを使用-

まずオーソドックスな方法です。
連想配列に格納後、Keyプロパティでキーの一覧を取得します。

前回ご紹介したコードとほぼ同じですが、連想配列の宣言の時に参照設定を使用する方法にしないとKeyプロパティは参照できないようです。

Sub DictionaryでKeyを使ってキーの一覧取得()

’開始時間の記載
Debug.Print “Dictionaryで項目入力開始-” & Time

’Dim dicJAN
’Set dicJAN = CreateObject(“Scripting.Dictionary”)
Dim dicJAN As New Scripting.Dictionary

’JAN と金額の合計をDictionaryに格納
Dim y, i
With Sheets(1)
y = .Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To y
’JANコードの登録がない場合、Dictionaryに追加
If Not dicJAN.Exists(.Cells(i, 1).Value) Then
dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)
’JANコードの登録がある場合、Dictionaryに格納されている値を更新
Else
dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)
End If
Next i
End With

’JAN一覧B列に合計金額の出力
Dim r
r = Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2))

For i = 0 To dicJAN.Count - 1
r(i + 2, 1) = dicJAN.Keys(i)
r(i + 2, 2) = dicJAN(r(i + 2, 1))
Next i

Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2)) = r

’終了時間の記載
Debug.Print “Dictionaryで項目入力終了-” & Time

End Sub

この方法で当初コードを書いていたのですが、Keys一覧の出力がものすごく遅くて悲しくなります。
せっかく連想配列を使用しているのが台無しになるレベルでした。

そのため、次の方法を検討してみました。

連想配列のキー一覧を出力する方法②-通常の配列を使用する

連想配列を使用する一番の理由は重複を判定するメソッドがあることですので、こちらを活用してキーを別の配列に格納する方法を考えました。

具体的にはキーとアイテムを連想配列に格納するタイミングで、キーを別の配列に格納しておいて、後でキーを格納した配列を呼び出すというフローです。

Sub Dictionaryでキーを配列に格納してから一覧取得()

’開始時間の記載
Debug.Print “配列で項目出力開始-” & Time
’Dim dicJAN
’Set dicJAN = CreateObject(“Scripting.Dictionary”)

Dim dicJAN As New Scripting.Dictionary
Dim x
x = 0
Dim JANコード一覧()

’JAN と金額の合計をDictionaryに格納
Dim y, i
With Sheets(1)
y = .Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To y
’JANコードの登録がない場合、Dictionaryに追加、JANコード一覧に登録
If Not dicJAN.Exists(.Cells(i, 1).Value) Then
dicJAN.Add .Cells(i, 1).Value, .Cells(i, 5)

’キーを「JANコード一覧」配列に格納
ReDim Preserve JANコード一覧(x)
JANコード一覧(x) = .Cells(i, 1).Value
x = x + 1

’JANコードの登録がある場合、Dictionaryに格納されている値を更新
Else
dicJAN(.Cells(i, 1).Value) = dicJAN(.Cells(i, 1).Value) + .Cells(i, 5)
End If
Next i
End With

’JAN一覧B列に合計金額の出力
With Sheets(2)
For i = 0 To dicJAN.Count - 1
.Cells(i + 2, 1) = JANコード一覧(i)
.Cells(i + 2, 2) = dicJAN(.Cells(i + 2, 1).Value)
Next i
End With

’終了時間の記載
Debug.Print “配列で項目出力終了-” & Time

End Sub

こちらのコードを実行すると明らかに先ほどのコードよりも高速なことが確認できました。
連想配列のキーを取り出す必要がある場合、別の配列にキーの一覧を格納して取得するフローが一番高速になると思います。

キー入力速度比較 Dictionary-Keys使用 配列使用
1回目 0:00:39 0:00:04
2回目 0:00:38 0:00:04
3回目 0:00:40 0:00:04
平均 0:00:39 0:00:04

連想配列を使用する際の最速の処理方法について

キーを取り出す場合以外に、①連想配列にキーとアイテムを格納する際や、②連想配列の一覧を出力する際も配列を使用することでより高速化することが可能です。

使用するのはセルを二次元配列に格納して一気に値を取得・値を入力する方法になります
興味がある方はこちらのページをご参照ください。

今回の処理においては1秒以下の実行時間まで短縮できました。
セルを配列に格納する方法は慣れればすごく有効な手段になりますので、使いこなせるならぜひ覚えていただくのをおすすめします。

Sub Dictionaryを使用する際に配列を使って最速の処理()

’セルを配列に格納した後にDicrionaryに登録・セルへの入力も配列に格納したものを一気に入力

’開始時間の記載
Debug.Print “配列と連想配列最速開始-” & Time
’Dim dicJAN
’Set dicJAN = CreateObject(“Scripting.Dictionary”)

Dim dicJAN As New Scripting.Dictionary
Dim x
x = 0
Dim JANコード一覧()
Dim y, i

’JAN と金額の合計をDictionaryに格納
Dim 参照範囲
y = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row
参照範囲 = Range(Sheets(1).Cells(1, 1), Sheets(1).Cells(y, 5))

For i = 2 To y
’JANコードの登録がない場合、Dictionaryに追加、JANコード一覧に登録
If Not dicJAN.Exists(参照範囲(i, 1)) Then
dicJAN.Add 参照範囲(i, 1), 参照範囲(i, 5)

’キーを「JANコード一覧」配列に格納
ReDim Preserve JANコード一覧(x)
JANコード一覧(x) = 参照範囲(i, 1)
x = x + 1

’JANコードの登録がある場合、Dictionaryに格納されている値を更新
Else
dicJAN(参照範囲(i, 1)) = dicJAN(参照範囲(i, 1)) + 参照範囲(i, 5)
End If
Next i

’JAN一覧B列に合計金額の出力
Dim 入力範囲
入力範囲 = Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2))

For i = 0 To dicJAN.Count - 1
入力範囲(i + 2, 1) = JANコード一覧(i)
入力範囲(i + 2, 2) = dicJAN(入力範囲(i + 2, 1))
Next i

Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(dicJAN.Count + 1, 2)) = 入力範囲

’終了時間の記載
Debug.Print “配列と連想配列最速終了-” & Time

End Sub

コメント