- 技術 💻
VBAの配列とDictionaryの使い方|セル一括読み書きと重複削除・集計
VBAの配列は「順番に並んだ表をまとめて持つ入れ物」、Dictionaryは「キーを指定して値を出し入れする入れ物(連想配列)」です。 セルの読み書きは配列で一度に済ませ、重複を除く・キーごとに数える・合計する処理はDictionaryに任せる、という組み合わせが基本の形になります。
この記事では、使い分けの早見表、セル範囲の一括読み込みから集計・一括書き戻しまでを1本にまとめた通しコード、メソッドとプロパティの一覧、エラー番号別の対処表を載せます。コードで使うメソッド・プロパティは、Microsoft Learn の VBA リファレンスで構文と引数を1つずつ確認したものだけにしています。
確認環境:Microsoft Learn の Office VBA リファレンス(英語版ページ)を 2026-10-05 に開いて、構文・引数・エラーの記載を照合しました。サンプルデータの集計値は、同じ集計を Python 3.11 で計算した値です。
この記事の結論
- 順番に並んだ表をそのまま扱うなら配列。セル範囲は
Range.Valueで2次元配列として一度に読み、加工してから一度に書き戻します。 - キーで探す・重複を除く・キーごとに数える/合計するなら Dictionary。
Existsで「もう登録済みか」をすぐ判定できます。 - 配列の添字は数字で決め打ちせず、
LBound〜UBoundで回す。下限が 0 の配列と 1 の配列が1つのコードに混ざるためです。 - Dictionary の
Addは同じキーで2回呼ぶとエラーになります。Existsで分岐するか、Itemへの代入で上書きします。 - Dictionary は VBA 本体ではなく Scripting ライブラリのオブジェクトです。VBA 本体だけで書きたいブックは配列と Collection でまとめます。
配列・Collection・Dictionaryの早見表
| 比べる点 | 配列 | Collection | Dictionary |
|---|---|---|---|
| 正体 | VBA の言語機能 | VBA のオブジェクト | Scripting ライブラリのオブジェクト(Scripting.Dictionary) |
| 作り方 | Dim a(1 To 3)、ReDim a(1 To n) |
Set c = New Collection |
Set d = CreateObject("Scripting.Dictionary") |
| キー検索 | 無い(添字の番号で取り出す) | 文字列のキーで取り出せる(キーは任意) | キーで取り出せる(配列以外なら何でもキーにできる) |
| 存在確認 | 自分でループして探す | 専用のメソッドは無い | Exists(key) が True / False を返す |
| 順序 | 添字の順 | 追加した順(before・after で挿入位置を指定できる) |
Keys・Items が配列で返る |
| 値の更新 | a(i) = 値 で代入 |
メンバーは Add・Item・Remove と Count のみ(入れ直しは Remove → Add) |
d.Item(key) = 値 で代入 |
| キーの一覧 | ― | 取り出すメソッドは無い | Keys |
| セルとの一括読み書き | Range.Value とそのままやり取りできる |
1件ずつ移す | Keys・Items を配列に移してから書く |
| 追加ライブラリ(Mac への持ち運び) | 不要 | 不要 | Scripting ライブラリを使う。Mac でも開くブックは配列と Collection で書くと持ち運びやすい |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VBAの配列とDictionaryの使い方|セル一括読み書きと重複削除・集計」
どれを使うかの判断表
| やりたいこと | 選ぶもの | 理由 |
|---|---|---|
| セルの表を読んで、計算して、書き戻す | 配列 | Range.Value が2次元配列を返し、2次元配列をそのまま代入できる |
| 重複を除いた一覧を作る | Dictionary | Exists で登録済みかを判定できる |
| キーごとに件数・合計を出す | Dictionary | Item(key) で値を読み、足して代入し直せる |
| 2つの列の組み合わせで集計する | Dictionary(複合キー) | 2つの値を区切り文字でつないだ文字列を1つのキーにできる |
| 件数が決まっていない物を順に貯める | Collection | Add するだけで増やせ、ReDim が要らない |
| 追加した順で途中に差し込む | Collection | Add の before・after で位置を指定できる |
| VBA 本体だけで完結させる | 配列 + Collection | どちらも別のライブラリを使わない |
配列の基本:添字・LBound・UBound・ReDim Preserve
添字の下限は「宣言のしかた」で決まる
配列の添字(番号)の下限は、宣言の書き方によって変わります。Microsoft のリファレンスでは、下限の既定値は 0 で、Option Base 1 をモジュールの先頭に書くと 1 になり、To を使えば任意の整数にできると説明されています。
Dim a(3) ' 下限は Option Base の設定に従う(既定は 0。0〜3 の4要素)
Dim b(1 To 3) ' To で明示。1〜3 の3要素
Dim c(1 To 5, 1 To 2) ' 2次元。5行×2列
| 配列の作り方 | 下限 | 公式ページの記載 |
|---|---|---|
Dim a(3) |
0(Option Base 1 があれば 1) |
Option Base ステートメント:既定の下限は 0 |
Dim b(1 To 3) |
1(To の左側の値) |
To 句で下限を明示できる |
Array(10, 20, 30) |
Option Base に従う。VBA.Array と書くと常に 0 |
Array 関数 |
Split("a,b,c", ",") |
0 | Split 関数:zero-based の1次元配列を返す |
d.Keys、d.Items |
公式の例は For i = 0 To d.Count - 1 で回している |
Keys メソッド、Items メソッド |
このように下限がそろわないので、ループは LBound(配列) から UBound(配列) までと書くのが安全です。2次元配列では UBound(配列, 1) が行方向、UBound(配列, 2) が列方向の上限です。
Dim i As Long
For i = LBound(b) To UBound(b)
Debug.Print i, b(i)
Next i
ReDim Preserveで中身を残したまま広げる
要素数があとから決まる配列は、Dim a() と大きさを書かずに宣言し、ReDim で大きさを決めます。Preserve を付けると、入っている値を残したまま大きさを変えられます。
Dim a() As Long
ReDim a(1 To 3)
a(1) = 10: a(2) = 20: a(3) = 30
ReDim Preserve a(1 To 5) ' 1〜3 の値は残り、4・5 が増える
Preserve には、リファレンスに明記された3つの決まりがあります。
| 決まり | 内容 |
|---|---|
| 変えられるのは最後の次元だけ | 2次元配列なら列方向(2番目の次元)の大きさだけを変えられる。次元の数は変えられない |
| 変えられるのは上限だけ | 下限を変えるとエラーになる |
| 小さくすると値がなくなる | 削った要素の値は残らない |
行を増やしていきたい2次元配列は、この決まりのため ReDim Preserve では広げられません。行数が先に分からないときは、Dictionary や Collection に貯めてから、件数が確定した時点で ReDim 出力(1 To 件数, 1 To 列数) と一度で確保すると、迷いが少なくなります。後半の通しコードはこの形です。
セル範囲を配列に一括で読み書きする
Range.Value は、2つ以上のセルを含む範囲に対して、セルの値が入った2次元配列を Variant で返します。逆に、2次元配列を Value に代入すると、値が1回の操作で範囲に写されます。セルを1つずつ読み書きする回数が減るので、行数が多い表ほど処理が軽くなります。
Sub DoubleQuantity()
Dim rng As Range
Dim v As Variant
Dim i As Long
Set rng = ThisWorkbook.Worksheets("申請").Range("C2:C13")
v = rng.Value ' 一括で読む(1列でも2次元配列)
For i = LBound(v, 1) To UBound(v, 1)
v(i, 1) = v(i, 1) * 2 ' 1列だけでも (行, 1) と2つの添字で指定する
Next i
rng.Value = v ' 一括で書き戻す
End Sub
覚えておきたい点は3つです。
- 1列だけ・1行だけ読んでも2次元配列です。
v(i)ではなくv(i, 1)(1行だけならv(1, j))と書きます。 - 2次元配列が返るのは、範囲が2セル以上のときです。1セルだけの範囲では配列にならないので、
IsArray(v)で確かめてから回します。 - 書き込み先の範囲が配列より大きいと、余ったセルにはエラー値が入るとリファレンスにあります。
Resize(UBound(v, 1), UBound(v, 2))で配列と同じ大きさにそろえてから代入します。
Dictionaryの基本:Add・Exists・Item・Keys
Dictionary は、キーと値(リファレンスでは item)の組を持つオブジェクトです。キーには配列以外なら何でも使えますが、文字列か整数にするのが一般的です。
Sub DictionaryBasics()
Dim d As Object
Dim k As Variant
Set d = CreateObject("Scripting.Dictionary")
d.Add "PC", 3 ' キーと値を追加
d.Add "モニター", 5
If d.Exists("PC") Then ' キーがあるか
d.Item("PC") = d.Item("PC") + 1 ' 値を読んで、足して、代入し直す
End If
d.Item("マウス") = 2 ' 無いキーへの代入は、新しいキーとして追加される
For Each k In d.Keys ' キーを順に取り出す
Debug.Print k, d.Item(k)
Next k
d.Remove "モニター" ' 1組を取り除く
Debug.Print d.Count ' 組の数
End Sub
Add と Item への代入は、同じキーがすでにあるときの動きが違います。 Add はエラーになり、Item(key) = 値 は上書きします。「初めて出てきたキーだけ登録したい」なら Exists で分岐して Add、「最後の値で上書きしてよい」なら Item への代入、と使い分けます。
通しコード:一括で読み、重複削除・件数・合計・複合キー集計をして、一括で書き戻す
備品の貸出申請を記録した表を例にします。シート名は「申請」、A列に部署、B列に品名、C列に数量が入っているとします。
| 部署 | 品名 | 数量 |
|---|---|---|
| 総務 | ノートPC | 2 |
| 開発 | モニター | 3 |
| 総務 | モニター | 1 |
| 営業 | ノートPC | 1 |
| 開発 | ノートPC | 4 |
| 開発 | モニター | 2 |
| 総務 | ノートPC | 1 |
| 営業 | マウス | 5 |
| 開発 | マウス | 2 |
| 営業 | ノートPC | 2 |
| 総務 | マウス | 3 |
| 開発 | モニター | 1 |
シートの A1 に貼り付けて試せるタブ区切り版です。
部署 品名 数量
総務 ノートPC 2
開発 モニター 3
総務 モニター 1
営業 ノートPC 1
開発 ノートPC 4
開発 モニター 2
総務 ノートPC 1
営業 マウス 5
開発 マウス 2
営業 ノートPC 2
総務 マウス 3
開発 モニター 1
コード全体
標準モジュールにそのまま貼り付けます。E列に「品名の重複を除いた一覧」、G〜I列に「部署ごとの件数と数量合計」、K〜M列に「部署×品名の数量合計」を書き出します。
Option Explicit
Sub SummarizeRequests()
Dim ws As Worksheet
Dim src As Variant ' 読み込んだ表(2次元配列)
Dim dicItem As Object ' 品名(重複を除く)
Dim dicCount As Object ' 部署 → 件数
Dim dicSum As Object ' 部署 → 数量の合計
Dim dicPair As Object ' 部署|品名 → 数量の合計
Dim i As Long
Dim dept As String, itemName As String, pairKey As String
Dim qty As Double
Set ws = ThisWorkbook.Worksheets("申請")
' --- 1. セル範囲を配列に一括で読む ---
src = ws.Range("A1").CurrentRegion.Value
If Not IsArray(src) Then
MsgBox "表が見つかりません。A1 から見出しとデータを入れてください。"
Exit Sub
End If
' --- 2. Dictionary を用意する ---
Set dicItem = CreateObject("Scripting.Dictionary")
Set dicCount = CreateObject("Scripting.Dictionary")
Set dicSum = CreateObject("Scripting.Dictionary")
Set dicPair = CreateObject("Scripting.Dictionary")
dicItem.CompareMode = vbTextCompare ' キーをテキスト比較で比べる
dicCount.CompareMode = vbTextCompare
dicSum.CompareMode = vbTextCompare
dicPair.CompareMode = vbTextCompare
' --- 3. 配列を1行ずつ回して Dictionary に貯める ---
For i = LBound(src, 1) + 1 To UBound(src, 1) ' +1 で見出し行を飛ばす
dept = src(i, 1)
itemName = src(i, 2)
qty = src(i, 3)
' 重複削除:初めて出てきた品名だけ登録する
If Not dicItem.Exists(itemName) Then
dicItem.Add itemName, i
End If
' 件数と合計:あれば足す、無ければ新しく登録する
If dicCount.Exists(dept) Then
dicCount.Item(dept) = dicCount.Item(dept) + 1
dicSum.Item(dept) = dicSum.Item(dept) + qty
Else
dicCount.Add dept, 1
dicSum.Add dept, qty
End If
' 複合キー:2つの値を区切り文字でつないで1つのキーにする
pairKey = dept & "|" & itemName
If dicPair.Exists(pairKey) Then
dicPair.Item(pairKey) = dicPair.Item(pairKey) + qty
Else
dicPair.Add pairKey, qty
End If
Next i
' --- 4. 結果を配列にまとめて、一括で書き戻す ---
ws.Range("E:M").ClearContents
WriteItems ws.Range("E1"), dicItem
WriteCountAndSum ws.Range("G1"), dicCount, dicSum
WritePairs ws.Range("K1"), dicPair
End Sub
' 重複を除いた一覧を1列に書く
Private Sub WriteItems(ByVal topLeft As Range, ByVal dic As Object)
Dim out() As Variant
Dim keys As Variant
Dim r As Long
ReDim out(1 To dic.Count + 1, 1 To 1)
out(1, 1) = "品名"
keys = dic.Keys
For r = LBound(keys) To UBound(keys)
out(r - LBound(keys) + 2, 1) = keys(r)
Next r
topLeft.Resize(UBound(out, 1), UBound(out, 2)).Value = out
End Sub
' キーごとの件数と合計を3列に書く
Private Sub WriteCountAndSum(ByVal topLeft As Range, _
ByVal dicCount As Object, ByVal dicSum As Object)
Dim out() As Variant
Dim keys As Variant
Dim r As Long, n As Long
ReDim out(1 To dicCount.Count + 1, 1 To 3)
out(1, 1) = "部署": out(1, 2) = "件数": out(1, 3) = "数量合計"
keys = dicCount.Keys
For r = LBound(keys) To UBound(keys)
n = r - LBound(keys) + 2
out(n, 1) = keys(r)
out(n, 2) = dicCount.Item(keys(r))
out(n, 3) = dicSum.Item(keys(r))
Next r
topLeft.Resize(UBound(out, 1), UBound(out, 2)).Value = out
End Sub
' 複合キーを2列に戻して、合計と合わせて3列に書く
Private Sub WritePairs(ByVal topLeft As Range, ByVal dic As Object)
Dim out() As Variant
Dim keys As Variant
Dim parts As Variant
Dim r As Long, n As Long
ReDim out(1 To dic.Count + 1, 1 To 3)
out(1, 1) = "部署": out(1, 2) = "品名": out(1, 3) = "数量合計"
keys = dic.Keys
For r = LBound(keys) To UBound(keys)
n = r - LBound(keys) + 2
parts = Split(keys(r), "|") ' 0 始まりの1次元配列
out(n, 1) = parts(0)
out(n, 2) = parts(1)
out(n, 3) = dic.Item(keys(r))
Next r
topLeft.Resize(UBound(out, 1), UBound(out, 2)).Value = out
End Sub
各行の役割
| 場所 | コード | 役割 |
|---|---|---|
| 1. 読む | ws.Range("A1").CurrentRegion.Value |
A1 を含む、空白行と空白列で囲まれたひとかたまりの範囲を2次元配列として一度に読む |
| 1. 読む | If Not IsArray(src) Then |
範囲が1セルだけのときは配列にならないので、その場合は処理を終える |
| 2. 用意 | CreateObject("Scripting.Dictionary") |
Dictionary を作る。用途ごとに1つずつ用意すると読みやすい |
| 2. 用意 | CompareMode = vbTextCompare |
キーの比べ方を「テキスト比較」にする。キーを1つも入れていないうちに設定する |
| 3. 回す | For i = LBound(src, 1) + 1 To UBound(src, 1) |
行方向の下限から上限まで回す。+ 1 は見出し行の分 |
| 3. 回す | If Not dicItem.Exists(itemName) Then dicItem.Add ... |
重複削除。登録済みの品名は読み飛ばす |
| 3. 回す | dicCount.Item(dept) = dicCount.Item(dept) + 1 |
件数の集計。今の値を読み、1 を足して代入し直す |
| 3. 回す | dicSum.Item(dept) = dicSum.Item(dept) + qty |
合計の集計。足す数を 1 から数量に変えただけ |
| 3. 回す | pairKey = dept & "|" & itemName |
複合キー。データに出てこない文字を区切りに選ぶ |
| 4. 書く | ws.Range("E:M").ClearContents |
前回の結果を消す。書式は残り、値と数式だけが消える |
| 4. 書く | ReDim out(1 To dic.Count + 1, 1 To 3) |
件数が確定したので、見出し1行+データ行の大きさで出力用の配列を一度で確保する |
| 4. 書く | keys = dic.Keys |
キーを配列で受け取る。下限は LBound(keys) で受ける |
| 4. 書く | Split(keys(r), "|") |
複合キーを区切り文字で分けて、部署と品名に戻す |
| 4. 書く | topLeft.Resize(UBound(out, 1), UBound(out, 2)).Value = out |
左上のセルを配列と同じ行数・列数に広げて、一度で書き込む |
このデータでの集計値
同じ集計を Python で計算した値です。VBA の実行画面を写したものではありません。行の並びは Keys が返す順に従うため、決まった順で見せたいときは書き戻したあとにシート側で並べ替えます。
品名の重複を除いた一覧(12行 → 3件)
| 品名 |
|---|
| ノートPC |
| モニター |
| マウス |
部署ごとの件数と数量合計
| 部署 | 件数 | 数量合計 |
|---|---|---|
| 総務 | 4 | 7 |
| 開発 | 5 | 12 |
| 営業 | 3 | 8 |
部署×品名の数量合計(8組)
| 部署 | 品名 | 数量合計 |
|---|---|---|
| 総務 | ノートPC | 3 |
| 総務 | モニター | 1 |
| 総務 | マウス | 3 |
| 開発 | ノートPC | 4 |
| 開発 | モニター | 6 |
| 開発 | マウス | 2 |
| 営業 | ノートPC | 3 |
| 営業 | マウス | 5 |
自分の表に合わせて変える場所
| 変えたいこと | 変える場所 |
|---|---|
| シート名 | Worksheets("申請") |
| 集計のキーにする列 | src(i, 1) の列番号 |
| 合計する列 | src(i, 3) の列番号 |
| 3つの列の組み合わせで集計 | pairKey = a & "|" & b & "|" & c とつなぎ、parts(2) まで取り出す |
| キーをバイナリ比較で比べる | CompareMode の値を vbBinaryCompare に変える |
| 書き出し先 | ws.Range("E1") などの左上セルと、ClearContents の範囲 |
Collectionで同じ重複削除を書く(VBA本体だけで完結)
Collection には Exists がありませんが、キーが重複する Add はエラーになるという決まりを使うと、重複を除いた一覧を作れます。エラーをいったん受け流し、登録できたものだけが残る形です。
Sub UniqueByCollection()
Dim src As Variant
Dim c As Collection
Dim out() As Variant
Dim i As Long
src = ThisWorkbook.Worksheets("申請").Range("A1").CurrentRegion.Value
If Not IsArray(src) Then Exit Sub
Set c = New Collection
On Error Resume Next ' キー重複のエラー 457 を受け流す
For i = LBound(src, 1) + 1 To UBound(src, 1)
c.Add src(i, 2), "k" & src(i, 2) ' 値とキーの両方に品名を入れる
Next i
On Error GoTo 0 ' エラーの受け流しをここで終える
ReDim out(1 To c.Count, 1 To 1)
For i = 1 To c.Count ' Collection は 1 番目から数える
out(i, 1) = c.Item(i)
Next i
ThisWorkbook.Worksheets("申請").Range("O1").Resize(c.Count, 1).Value = out
End Sub
Collection のキーは文字列で、キーの一覧を取り出すメソッドは無いので、値にも同じ内容を入れておくのがこつです。件数や合計の集計まで行うなら、値を読んで代入し直せる Dictionary のほうが短く書けます。
メソッド・プロパティ一覧
Dictionary
| 名前 | 種類 | 書き方 | 動き |
|---|---|---|---|
| Add | メソッド | d.Add key, item |
キーと値の組を追加する。キーがすでにあるとエラー |
| Exists | メソッド | d.Exists(key) |
キーがあれば True、無ければ False |
| Keys | メソッド | d.Keys |
すべてのキーを配列で返す |
| Items | メソッド | d.Items |
すべての値を配列で返す |
| Remove | メソッド | d.Remove key |
1組を取り除く。キーが無いとエラー |
| RemoveAll | メソッド | d.RemoveAll |
すべての組を取り除く |
| Item | プロパティ | d.Item(key)、d.Item(key) = 値 |
値を読む/代入する。代入時にキーが無ければ新しく作られる |
| Key | プロパティ | d.Key(key) = 新しいキー |
既存のキーを別のキーに付け替える。元のキーが無いとエラー |
| Count | プロパティ | d.Count |
組の数 |
| CompareMode | プロパティ | d.CompareMode = vbTextCompare |
キーの比べ方。データが入った後に変えるとエラー |
CompareMode に指定できる値は次のとおりです。
| 定数 | 値 | 意味 |
|---|---|---|
vbUseCompareOption |
-1 | Option Compare ステートメントの設定に従う |
vbBinaryCompare |
0 | バイナリ比較 |
vbTextCompare |
1 | テキスト比較 |
vbDatabaseCompare |
2 | Microsoft Access 専用 |
配列
| 名前 | 種類 | 書き方 | 動き |
|---|---|---|---|
| LBound | 関数 | LBound(配列, [次元]) |
指定した次元の添字の下限。次元を省くと 1 |
| UBound | 関数 | UBound(配列, [次元]) |
指定した次元の添字の上限。次元を省くと 1 |
| ReDim | ステートメント | ReDim a(1 To n) |
大きさを決め直す |
| ReDim Preserve | ステートメント | ReDim Preserve a(1 To n) |
中身を残して最後の次元の上限だけを変える |
| IsArray | 関数 | IsArray(変数) |
変数が配列なら True |
| Array | 関数 | Array(10, 20, 30) |
値の並びから配列を作って Variant で返す |
| Split | 関数 | Split(文字列, 区切り) |
文字列を区切って 0 始まりの1次元配列で返す |
| Option Base | ステートメント | Option Base 1 |
モジュール内の既定の下限を 0 か 1 に決める。モジュールに1回だけ、配列の宣言より前に書く |
Collection
| 名前 | 種類 | 書き方 | 動き |
|---|---|---|---|
| Add | メソッド | c.Add item, key, before, after |
メンバーを追加する。key・before・after は省略できる。before と after は同時に指定できない |
| Item | メソッド | c.Item(位置またはキー) |
位置(1 から)またはキーでメンバーを返す |
| Remove | メソッド | c.Remove 位置またはキー |
メンバーを取り除く |
| Count | プロパティ | c.Count |
メンバーの数 |
症状別の対処表
エラーメッセージは Microsoft Learn(英語版)の表記です。日本語版の Excel では日本語のメッセージで表示されます。
| 症状 | 原因 | 対処 |
|---|---|---|
セルから読んだ配列を For i = 0 To ... で回すとエラー 9(Subscript out of range) |
配列の下限が 0 とは限らない。存在しない添字を指定している | For i = LBound(v, 1) To UBound(v, 1) と書き、数字の決め打ちをやめる |
1列だけ読んだ配列を v(i) と書くとエラー 9 |
1列でも2次元配列なので、添字が2つ要る | v(i, 1) と書く |
範囲が1セルだけのとき UBound(v, 1) で止まる |
Range.Value が2次元配列を返すのは2セル以上の範囲のとき。1セルでは配列にならない |
If Not IsArray(v) Then で分岐する。1セルのときだけ ReDim した 1×1 の配列に入れ直す方法もある |
Add でエラー 457(This key is already associated with an element of this collection) |
同じキーで2回 Add した |
If Not d.Exists(key) Then d.Add key, 値 と分岐するか、d.Item(key) = 値 で上書きする |
Exists で確かめたつもりが、キーが勝手に増えている |
Item(key) は、読むだけでも、無いキーなら空の値で新しく作る |
有無を調べるときは必ず Exists を使い、Item で読む前に分岐する |
CompareMode の行でエラーになる |
すでにデータが入っている Dictionary の比べ方を変えようとした | CreateObject の直後、最初の Add より前に設定する |
ReDim Preserve でエラーになる |
最後の次元以外の大きさ、または下限を変えようとした | 行数は先に数えてから ReDim で一度に確保する。変えるのは最後の次元の上限だけにする |
Dim a() の直後に a(1) = ... でエラー 9 |
大きさをまだ決めていない | ReDim a(1 To n) で大きさを決めてから使う |
| 書き戻したら余ったセルにエラー値が並ぶ | 書き込み先の範囲が配列より大きい | Resize(UBound(v, 1), UBound(v, 2)) で配列と同じ大きさにそろえる |
Remove でエラーになる |
取り除こうとしたキーが無い | If d.Exists(key) Then d.Remove key と分岐する |
CreateObject("Scripting.Dictionary") でエラー 429(Component can't create object) |
Scripting ライブラリのオブジェクトを作れない環境で動かしている | 配列と Collection だけで書き直す(前の章のコード) |
確かめて分かったこと
Microsoft Learn のページを1つずつ開いて照合した結果、見落としやすかった点です。
Item(key)は読むだけでもキーを作ります。 Item プロパティのページに「既存の値を返そうとしてキーが見つからないときは、新しいキーが作られ、値は空のままになる」とあります。有無の判定にItemを使うと、判定のたびに空のキーが増えます。CompareModeは空のうちにしか変えられません。 データが入った Dictionary の比較方法を変えるとエラーになる、と明記されています。Array関数の下限はOption Baseの影響を受けます。VBA.Arrayと書いた場合だけ、Option Baseに関係なく 0 になります。Range.Valueのページには、返ってくる配列の下限の数字が書かれていません。 公式の例はLBound〜UBoundで回しているので、この記事のコードも同じ書き方にそろえました。ReDim Preserveで下限を変えるとエラーになります。 「最後の次元だけ」は知られていますが、上限しか動かせない点も同じ段落に書かれています。- Collection のメンバーは
Add・Item・Remove・Countの4つです。 存在確認やキー一覧のメソッドが無いことが、Dictionary を選ぶ一番の理由になります。
よくある質問
Q. VBAの配列とDictionaryの違いは? A. 配列は番号(添字)で、Dictionary はキーで値を取り出します。表をそのまま読み書きするなら配列、キーで探す・重複を除く・集計するなら Dictionary が向いています。
Q. DictionaryとCollectionの違いは?
A. Dictionary には存在確認の Exists、キー一覧の Keys、値の代入があり、Collection にはありません。Collection は VBA 本体のオブジェクトなので、別のライブラリを使わずに済みます。
Q. Dictionaryで重複エラーが出るときは?
A. Add の前に Exists で分岐すれば解消します。上書きでよい場合は d.Item(key) = 値 と代入します。
Q. セルの読み書きを速くするには?
A. Range.Value で配列に一度で読み、配列の中で計算して、同じ大きさの範囲に一度で書き戻します。セルに触れる回数が、行数分から2回に減ります。
Q. 参照設定は必要ですか?
A. この記事のコードは CreateObject("Scripting.Dictionary") で作り、変数を As Object で宣言しているので、参照設定をしなくても書けます。
まとめ
- 順番に並んだ表は配列、キーで探す・重複を除く・集計するなら Dictionary。
- セルは
Range.Valueで一括で読み、同じ大きさにResizeして一括で書き戻す。 - 添字は
LBound〜UBound。1列だけ読んでも2次元配列、1セルだけでは配列にならない。 Addは重複でエラー、Itemへの代入は上書き。有無の判定は必ずExists。CompareModeは最初のAddより前に設定する。- 行数が分からない出力は、Dictionary に貯めて件数が決まってから
ReDimで一度に確保する。 - VBA 本体だけで書きたいときは配列と Collection で同じ重複削除ができる。
参考・出典
- Microsoft Learn「Dictionary object」「Add method (Dictionary)」「Exists method」「Keys method」「Items method」「Remove method (Dictionary object)」「Item property (Dictionary object)」「Key property」「CompareMode property」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/dictionary-object
- Microsoft Learn「Collection object」「Add method (Visual Basic for Applications)」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/collection-object
- Microsoft Learn「LBound function」「UBound function」「ReDim statement」「Option Base statement」「Array function」「Split function」「IsArray function」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/redim-statement
- Microsoft Learn「Range.Value property (Excel)」「Range.Resize property (Excel)」「Range.CurrentRegion property (Excel)」「Range.ClearContents method (Excel)」 https://learn.microsoft.com/en-us/office/vba/api/excel.range.value
- Microsoft Learn「Trappable errors」「Subscript out of range (Error 9)」「This key is already associated with an element of this collection (Error 457)」 https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/trappable-errors
- Microsoft Learn「Office for Mac」 https://learn.microsoft.com/en-us/office/vba/api/overview/office-mac
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「VBAの配列とDictionaryの使い方|セル一括読み書きと重複削除・集計」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。