【入門編】【配列の完全複製(ディープコピー)】参照渡しによるデータ破壊を防ぐ多次元配列・連想配列のクローン処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは、現場の最前線で自動化の荒波を乗り越えてきたチーフアーキテクトです。

VBScriptを使いこなし、マクロの記録を超えて「真の自動化」を目指すあなたが、必ず一度はぶつかる壁があります。それが「変数をコピーしたはずなのに、元のデータまで書き換わってしまった!」という怪奇現象です。

これはバグではなく、VBScriptのメモリ管理における「参照渡し」という仕組みが原因です。ここを理解し、制御できるようになれば、あなたのコードの堅牢性は劇的に向上します。

今日は、多次元配列や連想配列(Dictionary)を、元のデータを汚さずに「完全に独立させて複製」するディープコピー(深い複製)の極意を伝授しましょう。ここをクリアすれば、VBScriptの基本はバッチリですよ。

1. なぜ「代入」だけではダメなのか?(参照の罠)

まずは、多くの人が陥る失敗例を見てみましょう。

‘ 失敗する例
Dim ArrayA, ArrayB
ArrayA = Array(“りんご”, “みかん”, “バナナ”)

‘ ArrayAをArrayBにコピーしたつもり…
ArrayB = ArrayA

‘ ArrayBの値を書き換えると…
ArrayB(0) = “メロン”

‘ 【衝撃の結果】ArrayA(0) も “メロン” に変わってしまう!
WScript.Echo ArrayA(0)

なぜこうなるのか?

VBScriptにおいて、配列やオブジェクトを変数に代入すると、中身そのものではなく「データの置いてある場所(メモリのアドレス)」だけがコピーされます。これを「参照渡し(浅いコピー)」と呼びます。

一つの家(データ)に対して、二つの住所録(変数AとB)がある状態です。どちらの住所録を使って家の中をリフォームしても、結果は同じ家が変わってしまいますよね。

これを防ぐには、別の場所に「全く同じ家をもう一軒建てる(ディープコピー)」必要があるのです。

2. 1次元配列のディープコピー

1次元配列であれば、最も確実なのは「新しい配列を宣言し、中身を一つずつ移し替える」ことです。

””’
‘ 1次元配列を完全に複製する関数
””’
Function CloneArray1D(ByVal sourceArray)
Dim i, targetArray
‘ 同じサイズの配列を準備
ReDim targetArray(UBound(sourceArray))

For i = 0 To UBound(sourceArray)
targetArray(i) = sourceArray(i)
Next

CloneArray1D = targetArray
End Function

3. 難敵:多次元配列のクローン処理

Excelのセルデータなどを一括で扱う際によく使う「多次元配列」。これを複製するには、次元数(UBoundの第2引数など)を正確に把握してループを回す必要があります。

””’
‘ 2次元配列を完全に複製する関数
””’
Function CloneArray2D(ByVal sourceArray)
Dim i, j, targetArray
Dim rows, cols

rows = UBound(sourceArray, 1)
cols = UBound(sourceArray, 2)

‘ 同じ次元で再定義
ReDim targetArray(rows, cols)

For i = 0 To rows
For j = 0 To cols
targetArray(i, j) = sourceArray(i, j)
Next
Next

CloneArray2D = targetArray
End Function

4. 盲点:Scripting.Dictionary(連想配列)の複製

設定情報やマスターデータを保持するのに便利な `Scripting.Dictionary` ですが、これも単純な代入では参照がコピーされるだけです。

連想配列の複製は、「すべてのキーをループで回して、新しいDictionaryにセットする」のが正解です。

””’
‘ Dictionaryオブジェクトを完全に複製する関数
””’
Function CloneDictionary(ByVal sourceDict)
Dim targetDict, key
Set targetDict = CreateObject(“Scripting.Dictionary”)

‘ 元のDictionaryのキーをすべて走査
For Each key In sourceDict.Keys
‘ 要素がオブジェクトの場合はさらに注意が必要ですが、
‘ 基本的な文字列や数値ならこれで完全に独立します
targetDict.Add key, sourceDict(key)
Next

Set CloneDictionary = targetDict
End Function

5. 【究極】あらゆるデータ型を判別して複製する「万能クローン関数」

実務では、渡される変数が配列なのか、Dictionaryなのか、あるいはただの数値なのか分からないことがあります。そんな時に役立つ、チーフアーキテクト特製の「汎用ディープコピー・プロトコル」を公開します。

これを標準モジュールに忍ばせておけば、データ破壊の恐怖から解放されます。

””’
‘ 汎用ディープコピー関数 (DeepClone)
‘ 配列、Dictionary、値型を自動判別して完全に独立したコピーを返します
””’
Function DeepClone(ByVal source)
If IsArray(source) Then
‘ — 配列の場合 —
Dim i, j, targetArray
On Error Resume Next
Dim dims: dims = 0
‘ 次元数をチェック
Do
dims = dims + 1
Call UBound(source, dims)
Loop While Err.Number = 0
Err.Clear
dims = dims – 1

If dims = 1 Then
ReDim targetArray(UBound(source))
For i = 0 To UBound(source): targetArray(i) = DeepClone(source(i)): Next
ElseIf dims = 2 Then
ReDim targetArray(UBound(source, 1), UBound(source, 2))
For i = 0 To UBound(source, 1)
For j = 0 To UBound(source, 2): targetArray(i, j) = DeepClone(source(i, j)): Next
Next
End If
DeepClone = targetArray

ElseIf IsObject(source) Then
‘ — オブジェクトの場合 —
If TypeName(source) = “Dictionary” Then
Dim targetDict, k
Set targetDict = CreateObject(“Scripting.Dictionary”)
For Each k In source.Keys
‘ 再帰的にクローン(中身が配列の場合も考慮)
If IsObject(source(k)) Then
Set targetDict(k) = DeepClone(source(k))
Else
targetDict(k) = DeepClone(source(k))
End If
Next
Set DeepClone = targetDict
Else
‘ その他のオブジェクトは参照コピー(必要に応じて拡張)
Set DeepClone = source
End If

Else
‘ — 基本データ型(数値・文字列)の場合 —
DeepClone = source
End If
End Function

陥りやすいエラーと回避策

1. Subscript out of range (インデックスが範囲外です)

  • `UBound` で取得したサイズと、`ReDim` するサイズがズレている時に発生します。必ず `0 To UBound(…)` の範囲を意識しましょう。

2. Object required (オブジェクトが必要です)

  • Dictionaryなどを複製して返す際、受け取り側で `Set` を忘れると発生します。
  • `Set newDict = DeepClone(oldDict)` と書くのを忘れないでください。

3. メモリの肥大化

  • 巨大な配列を何度もディープコピーすると、メモリを消費します。本当に「独立したコピー」が必要な場面(元データを残したまま加工したい時など)に絞って使いましょう。

まとめ

VBScriptにおける「代入」は、あくまで「名札の付け替え」に過ぎないことが多いです。
特に「元の配列をバックアップとして残しておきたい」という場面では、今回紹介したループによる移し替えが必須となります。

一見遠回りに見える「一つずつループでコピーする」という手法こそが、メモリ上のデータを確実に掌握し、予期せぬバグを防ぐ唯一の道なのです。

このロジックをマスターすれば、あなたのスクリプトは格段にプロフェッショナルなものへと進化します。一歩ずつ、確実にコードを自分の支配下に置いていきましょう。応援していますよ!

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