【ExcelVBA・マクロ】セル範囲を配列に一括代入して高速化する方法|Range⇔配列変換の基本【コピペOK】

ExcelVBA
スポンサーリンク
スポンサーリンク

はじめに

ExcelVBAで大量のデータを処理していると、「セルを1つずつループして処理しているだけなのに、なぜか異常に時間がかかる」という経験はありませんか? その原因の多くは、セルへのアクセスを繰り返していることにあります。

VBAでは、セル1つにアクセスするたびにExcelのアプリケーション本体とやり取りが発生し、これが数千行・数万行になると大きな処理コストになります。この問題を解決する定番の方法が、セル範囲を配列に一括代入してから処理するというテクニックです。この記事では、Range⇔配列変換の基本と、実際にどれくらい速くなるのかを解説します。

なぜセルを1つずつ処理すると遅いのか

以下のような「セルを1つずつループして値を書き込む」コードを考えてみます。

Sub SlowWrite()
    Dim i As Long
    Dim startTime As Double
    startTime = Timer

    For i = 1 To 50000
        Cells(i, 1).Value = i * 2
    Next i

    MsgBox "処理時間:" & Format(Timer - startTime, "0.00") & "秒"
End Sub

このコードは、Cells(i, 1)にアクセスするたびにExcelとVBAの間で通信が発生します。5万回ループすると、この通信コストが積み重なり、処理に数秒〜数十秒かかることもあります。

配列を使った高速化の基本

配列を使う場合は、以下の3ステップで処理します。

  1. セル範囲の値をまとめて配列に読み込む
  2. 配列の中でループ処理を行う(メモリ上の操作なので高速)
  3. 配列の内容をまとめてセル範囲に書き戻す
Sub FastWrite()
    Dim i As Long
    Dim startTime As Double
    Dim dataArray() As Variant

    startTime = Timer

    ' 1. 配列を用意(1列×50000行)
    ReDim dataArray(1 To 50000, 1 To 1)

    ' 2. 配列内で計算(メモリ上の操作)
    For i = 1 To 50000
        dataArray(i, 1) = i * 2
    Next i

    ' 3. 配列の内容をまとめてセル範囲に書き込む
    Range("A1:A50000").Value = dataArray

    MsgBox "処理時間:" & Format(Timer - startTime, "0.00") & "秒"
End Sub

SlowWriteFastWriteを実際に比較すると、後者は数倍〜数十倍速くなることが多く、データ件数が増えるほどその差は顕著になります。

セル範囲を配列に読み込む方法

既存のセル範囲の値をまとめて配列に読み込みたい場合は、Range.Valueを配列変数に代入するだけです。

Sub ReadToArray()
    Dim dataArray As Variant
    Dim i As Long

    ' セル範囲A1:C100の値をまとめて配列に読み込む
    dataArray = Range("A1:C100").Value

    ' 配列は2次元(行, 列)になる
    For i = 1 To UBound(dataArray, 1)
        Debug.Print dataArray(i, 1) & " / " & dataArray(i, 2) & " / " & dataArray(i, 3)
    Next i
End Sub

ここでのポイントは、dataArrayVariant型で宣言しておくことです。Range.Valueを代入すると、自動的に「1行目下限, 1列目下限」が1から始まる2次元配列になります。

実務での活用例:条件に合うデータだけを抽出する

配列処理は、単純な書き込みだけでなく「条件判定をしながら別シートに転記する」ような処理でも威力を発揮します。

Sub ExtractHighScore()
    Dim srcData As Variant
    Dim resultData() As Variant
    Dim i As Long, count As Long
    Dim lastRow As Long

    lastRow = Cells(Rows.Count, 1).End(xlUp).Row
    srcData = Range("A2:B" & lastRow).Value   ' A列:氏名、B列:点数

    ReDim resultData(1 To UBound(srcData, 1), 1 To 2)
    count = 0

    For i = 1 To UBound(srcData, 1)
        If srcData(i, 2) >= 80 Then
            count = count + 1
            resultData(count, 1) = srcData(i, 1)
            resultData(count, 2) = srcData(i, 2)
        End If
    Next i

    If count > 0 Then
        Worksheets("結果").Range("A2").Resize(count, 2).Value = resultData
    Else
        MsgBox "条件に合うデータがありませんでした。"
    End If
End Sub

セルを1つずつ判定しながらシート間を行き来するコードと比べて、配列内で完結させてから最後にまとめて書き込む方が圧倒的に高速です。

配列を使う際の注意点

  • Range.Valueで読み込んだ配列は、行・列ともに1から始まる(0からではない)
  • 配列に書き戻す際、範囲のサイズと配列のサイズを一致させる必要がある(Resizeで調整すると安全)
  • セル1個だけを対象にした場合、Range("A1").Valueは配列ではなく単一の値になる点に注意
  • 数式が入っているセルを配列に読み込むと、数式ではなく計算結果の値が格納される

まとめ

この記事では、ExcelVBAでセル範囲を配列に一括代入して処理を高速化する方法を解説しました。

  • セルへの1件ずつのアクセスは、件数が増えるほど処理コストが大きくなる
  • Range.Valueを配列に代入すれば、セル範囲の値をまとめて読み込める
  • 配列内でループ処理を行い、最後にRange.Value = 配列でまとめて書き戻すと高速
  • 条件抽出やデータ加工など、大量データを扱う処理ほど効果が大きい

大量データを扱うマクロで動作が遅いと感じたら、まずは「セルに直接アクセスしている箇所を配列処理に置き換えられないか」を見直してみることをおすすめします。

スポンサーリンク
スポンサーリンク
ExcelVBA
シェアする
いがぴをフォローする

コメント

タイトルとURLをコピーしました