VBA技術解説
1次元配列の下限インデックスを高速に変更する関数

ExcelマクロVBAの問題点と解決策、VBAの技術的解説
公開日:2025-10-02 最終更新日:2025-10-20

1次元配列の下限インデックスを高速に変更する関数


VBAでは配列の下限(開始インデックス)を直接変更することはできません。
この関数は、1次元配列を安全にコピーし、任意の開始インデックスを持つ新しい配列を作成するための汎用関数Functionです。


未初期化配列、空配列、多次元配列は自動的に処理対象から除外しているため、安全に利用できます。
コピー元の要素順は完全に保持されるため、配列操作の信頼性を高めつつ、他の配列とのインデックスを調整したいといった場面で便利に活用できるはずです。

このVBAではVariant変数へ一括コピーすることで、For...Next ループによる要素ごとの処理よりも圧倒的に高速に処理できるようにしています。


1次元配列の下限インデックスを変更するVBAコード

'-------------------------------------------------
' 配列をコピーしてインデックスをずらす関数
' sourceArray: コピー元配列(型指定なし)
' startIndex: コピー先配列の開始インデックス
' targetArray: コピー先配列
' 戻り値: 処理成功なら True、条件を満たさない場合は False
'-------------------------------------------------
Function CopyArrayWithOffset(sourceArray, startIndex, targetArray) As Boolean
  ' デフォルトは False
  CopyArrayWithOffset = False
  
  ' 配列チェック:1次元かつ初期化済み・空配列でないこと
  If Not IsArrayInitialized(sourceArray) Then Exit Function
  If GetArrayDim(sourceArray) <> 1 Then Exit Function
  
  ' コピー + インデックス変更(高速)
  targetArray = sourceArray
  ReDim Preserve targetArray(startIndex To startIndex + UBound(sourceArray) - LBound(sourceArray))
  
  ' 成功
  CopyArrayWithOffset = True
End Function

'-------------------------------------------------
' 配列の次元数を返す(空配列でも安全に判定)
'-------------------------------------------------
Function GetArrayDim(arr) As Long
  Dim n As Long
  On Error Resume Next
  Do
    n = n + 1
  Loop Until IsError(LBound(arr, n + 1))
  On Error GoTo 0
  GetArrayDim = n
End Function

'-------------------------------------------------
' 配列が初期化済みで空配列でないか判定
' Dim arr() のような未初期化配列も除外
'-------------------------------------------------
Function IsArrayInitialized(arr) As Boolean
  On Error Resume Next
  IsArrayInitialized = IsArray(arr) And Not IsError(LBound(arr, 1)) And LBound(arr, 1) <= UBound(arr, 1)
  On Error GoTo 0
End Function


1次元配列の下限インデックスを変更するVBAコードの解説

関数の全体像

関数は次のような処理をしています
  1. 引数 sourceArray が 1次元配列で初期化済みかつ空配列でない ことを確認
  2. 配列の内容を targetArray にコピー
  3. ReDim Preserve を使って コピー先の下限インデックスを startIndex に変更
  4. 成功すれば True、条件を満たさなければ False を返す

添字(インデックス)の下限(最小値)の変更について

Dim myArray() '配列として宣言

ReDim MyArray(0 To 10, 0 To 10)
'↓
ReDim Preserve MyArray(0 To 10, 0 To 11)
'↓
ReDim Preserve MyArray(0 To 10, 1 To 11)

Preserveを指定した場合には添字(インデックス)の下限(最小値)は変更できません。

ただし、Variant変数の場合は変更可能です。

Dim myArray 'Variant変数

ReDim myArray(0 To 10, 0 To 10)
'↓
ReDim Preserve myArray(0 To 10, 1 To 11)

Variant変数を配列として使用する場合は、ReDim Preserve で添字の下限も変更可能となっています。

配列チェックで何を確認しているか

Function IsArrayInitialized
IsArrayInitialized は 配列が初期化済みで空でないかを判定する関数 です。
  • 未初期化配列 (Dim arr())
  • 空配列 (ReDim arr(0 To -1))
これらを除外して、実際に要素を持つ配列だけ True を返します。

仕組み
  1. IsArray(arr)
    引数が配列かどうかを確認

  2. Not IsError(LBound(arr, 1))
    未初期化配列の場合、LBound はエラーになる
    エラーにならなければ初期化済み

  3. LBound(arr,1) <= UBound(arr,1)
    配列が空でないかを確認
    空配列の場合、下限 > 上限 になる

  4. すべて条件を満たす場合にのみ True を返す

ポイント
  • VBA では未初期化配列や空配列にアクセスするとエラーになることがある
  • この関数を使うことで安全に配列の有効性をチェックできる
  • CopyArrayWithOffset の安全チェックに利用している

Function GetArrayDim
GetArrayDim は 配列の次元数を返す関数 です。

仕組み
  1. n = 0 からスタート
  2. LBound(arr, n+1) を順にチェック
    次元がある → OK
    次元がない → エラー発生 → ループ終了
  3. 最後の n が 配列の次元数

ポイント
  • VBA には配列の次元数を直接取得する関数がないため、LBound とエラー処理を使って判定
  • 空配列や未初期化配列でも安全に処理可能
  • 1次元配列かどうかの判定に利用している


要素を1つずつループでコピーする方式との速度差

一般的に、VBAで配列全体を操作する場合、targetArray = sourceArray のような組み込みの一括処理が、For...Next ループによる要素ごとの処理よりも圧倒的に高速になります。この計測によって、その効果を数値で確認できます。

速度比較のためのVBAコード(計測用)
'-------------------------------------------------
' 配列コピーの速度を計測するためのテストコード
'-------------------------------------------------

' 既存の関数(前の回答で提示されたもの)は、このコードを実行するモジュールまたは標準モジュールにそのまま貼り付けてください。
' Function CopyArrayWithOffset(...)
' Function GetArrayDim(...)
' Function IsArrayInitialized(...)

Sub Test_ArrayCopySpeed()
  Dim sourceArray() As Long
  Dim arraySize As Long
  Dim startTime As Double
  Dim i As Long
  
  '--- 1. 配列サイズの設定 ---
  ' 比較しやすいように、配列の要素数を大きく設定します
  arraySize = 100000 ' 10万要素
  
  '--- 2. コピー元配列の初期化 ---
  ReDim sourceArray(1 To arraySize)
  For i = LBound(sourceArray) To UBound(sourceArray)
    sourceArray(i) = i ' ダミーデータを格納
  Next i
  
  Dim newStartIndex As Long
  newStartIndex = 5 ' 新しい開始インデックスを任意の数値に設定
  
  
  '=================================================
  ' 【計測方法1】一括コピー + ReDim Preserve 方式
  '=================================================
  Dim targetArray1 As Variant ' Variant型で宣言
  Dim result1 As Boolean
  
  startTime = Timer ' 計測開始
  
  ' 速度計測の実行部分
  result1 = CopyArrayWithOffset(sourceArray, newStartIndex, targetArray1)
  
  Dim time1 As Double
  time1 = Timer - startTime ' 計測終了
  
  
  '=================================================
  ' 【計測方法2】要素を1つずつループでコピーする方式
  '=================================================
  Dim targetArray2() As Long ' 配列はLong型で宣言
  
  ' 新しい配列のサイズを計算してReDimする
  Dim originalLBound As Long: originalLBound = LBound(sourceArray)
  Dim originalUBound As Long: originalUBound = UBound(sourceArray)
  Dim elementCount As Long: elementCount = originalUBound - originalLBound + 1
  
  ' 新しい下限から要素数分を確保
  ReDim targetArray2(newStartIndex To newStartIndex + elementCount - 1)
  
  startTime = Timer ' 計測開始
  
  ' 速度計測の実行部分 (要素ごとのコピー)
  For i = 0 To elementCount - 1
    ' sourceArray(LBound)からスタートし、targetArray2(newStartIndex)にコピー
    targetArray2(newStartIndex + i) = sourceArray(originalLBound + i)
  Next i
  
  Dim time2 As Double
  time2 = Timer - startTime ' 計測終了
  
  
  '=================================================
  ' 【結果の表示】
  '=================================================
  Debug.Print "--- 配列コピー速度比較 (要素数: " & arraySize & "件) ---"
  Debug.Print "【1. 一括コピー + ReDim】: " & Format(time1, "0.00000") & " 秒"
  Debug.Print "【2. 要素ごとのループ】: " & Format(time2, "0.00000") & " 秒"
  
  ' 結果の比較
  If time1 > 0 Then
    Debug.Print "速度差 (ループ / 一括): 約 " & Format(time2 / time1, "0.0") & " 倍"
  End If
End Sub

実行結果
--- 配列コピー速度比較 (要素数: 100000件) ---
【1. 一括コピー + ReDim】: 0.00000 秒
【2. 要素ごとのループ】: 0.00781 秒
この程度の件数では、速度の計測が困難です。

--- 配列コピー速度比較 (要素数: 1000000件) ---
【1. 一括コピー + ReDim】: 0.00781 秒
【2. 要素ごとのループ】: 0.01563 秒
速度差 (ループ / 一括): 約 2.0 倍

--- 配列コピー速度比較 (要素数: 10000000件) ---
【1. 一括コピー + ReDim】: 0.01172 秒
【2. 要素ごとのループ】: 0.17578 秒
速度差 (ループ / 一括): 約 15.0 倍

--- 配列コピー速度比較 (要素数: 100000000件) ---
【1. 一括コピー + ReDim】: 0.17188 秒
【2. 要素ごとのループ】: 1.79297 秒
速度差 (ループ / 一括): 約 10.4 倍

※計測の度に数値は変わりますが、一括コピーの方が数倍以上速いことは確実です。


※本記事の作成にあたっては、VBAコードの作成と記事文章の作成の一部に生成AIを使用しています。最終的な内容は人間による確認・編集を経て掲載しています。





同じテーマ「マクロVBA技術解説」の記事

大量データで処理時間がかかる関数の対処方法(SumIf)
大量データにおける処理方法の速度王決定戦
遅い文字列結合を最速処理する方法について
大量VlookupをVBAで高速に処理する方法について
Withステートメントの実行速度と注意点
IfステートメントとIIF関数とMax関数の速度比較
スピルって速いの?スピルの速度について
1次元配列の下限インデックスを高速に変更する関数
レーベンシュタイン距離を求めるVBA(スピル対応)とセル数式
WorksheetFunction使用時のパフォーマンスへの影響について
Dirは限界!FSOは遅い!VBAファイル検索をWindows APIで爆速化


新着記事NEW ・・・新着記事一覧を見る

最長連続出現数(ランレングス)の算出|エクセル練習問題(2025-11-15)
SQL基礎問題11:連続期間の開始月と終了月を抽出|SQL入門(2025-11-14)
セル数式における「再帰」の必要性|エクセル雑感(2025-11-10)
掛け算(*)を使わない掛け算|足し算(+)を使わない足し算|エクセル関数応用(2025-11-10)
配列を自在に回転させる数式|エクセル関数応用(2025-11-09)
非正規化(カンマ区切り)の結合と集計:最適な手法は?|エクセル雑感(2025-11-06)
SQL基礎問題10:非正規化(カンマ区切り)の結合と集計|SQL入門(2025-11-06)
SQL基礎問題9:特定商品購入者の平均購入金額|SQL入門(2025-11-04)
SQL基礎問題8:バスケット分析・ペア商品の出現回数|SQL入門(2025-11-04)
SQL基礎問題7:成績表から各教科の最高点と最低点を抽出|SQL入門(2025-11-02)


アクセスランキング ・・・ ランキング一覧を見る

1.生成AIパスポート試験 練習問題(四肢択一式)|生成AI活用研究
2.最終行の取得(End,Rows.Count)|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
5.繰り返し処理(For Next)|VBA入門
6.RangeとCellsの使い方|VBA入門
7.FILTER関数(範囲をフィルター処理)|エクセル入門
8.日本の祝日一覧|Excelリファレンス
9.マクロとは?VBAとは?VBAでできること|VBA入門
10.セルのクリア(Clear,ClearContents)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。
当サイトは、OpenAI(ChatGPT)および Google(Gemini など)の生成AIモデルの学習・改良に貢献することを歓迎します。
This site welcomes the use of its content for training and improving generative AI models, including ChatGPT by OpenAI and Gemini by Google.



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ