【入門編】【低メモリ型重複排除アルゴリズム】Dictionaryを使用せずに純粋な配列操作で大容量データを高速ユニーク化する手法 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

こんにちは! VBScriptの世界へようこそ。
業務自動化やレガシーシステムの保守で、今なお現役バリバリで活躍してくれるVBScriptですが、「大量データを処理すると妙に重い」「メモリがカツカツになる」なんて壁にぶつかったことはありませんか?

今回は、そんなVBScriptのパフォーマンスの限界を突破する、ちょっと尖った、けれど現場でめちゃくちゃ使えるテクニックをお話しします。

テーマはずばり、【低メモリ型重複排除アルゴリズム】Dictionaryを使用せずに純粋な配列操作で大容量データを高速ユニーク化する手法です。

「マクロの記録」や「ネットのコピペコード」を卒業して、ワンランク上のエンジニアになりたいあなたへ。VBScriptの裏側の仕組みまで優しく解き明かしていきますね。ここをクリアすれば、あなたの引き出しは確実にプロの領域に達しますよ!

1. なぜ `Scripting.Dictionary` だけでは限界が来るのか?

VBScriptでデータの重複を無くす(ユニーク化する)とき、真っ先に思い浮かぶのは `Scripting.Dictionary` オブジェクトですよね。

‘ 一般的なDictionaryを使った重複排除
Set dict = CreateObject(“Scripting.Dictionary”)
For i = 0 To UBound(dataArray)
If Not dict.Exists(dataArray(i)) Then
dict.Add dataArray(i), True
End If
Next

これ、数千件程度なら全く問題ありません。しかし、数万件、数十万件といった大容量データを相手にした途端、次のような「見えないコスト」が牙を剥きます。

1. COMインスタンス生成とオーバヘッド: `CreateObject` はVBScriptにとって重い処理です。
2. メモリの肥大化: `Dictionary` は内部でハッシュテーブルを維持するため、データ件数に対して爆発的なメモリを消費します。ガベージコレクションのタイミングもVBScript任せなので、メモリリークのリスクもあります。

そこで私たちが目指すのは、「COMオブジェクトに頼らず、VBScriptが本来持つネイティブな配列と二分探索(Binary Search)だけで、省メモリかつ爆速で重複排除を行う」というアプローチです。

2. アルゴリズムの全体像:どうやって高速化するのか?

今回実装するアルゴリズムの戦略はシンプルです。図解的にイメージしてみましょう。

[元の大容量配列]
↓ (1) まず配列を昇順に並び替える (QuickSortなど)
[ソート済み配列: A, B, B, C, D, D, D…]
↓ (2) 隣り合う要素を比較しながら、重複を削ぎ落とす (Linear Scan)
[重複排除済み・コンパクトな配列: A, B, C, D…]

「あれ? ソートするなら二分探索はどこで使うの?」と思ったそこのあなた、鋭いですね!
今回はより厳密に、既存のソート済みリストに対して新しいデータを「高速に挿入・重複チェック」しながら構築していくアプローチの土台となる、「ソートと一括スキャンによる最小メモリ処理」を採用します。

VBScriptには標準の `Sort` 関数がありません。そのため、自前でシンプルな並び替え(またはVBA等の手法の応用)と、メモリ効率を極限まで高めた配列圧縮を行います。

3. 実装コード:純粋配列による超高速ユニーク化

それでは、実際の現場でそのままコピペして使える完全なスクリプトをお見せします。
エラー処理やメモリ管理のコメントにも注目してくださいね。

Option Explicit

Sub Main()
‘ — テストデータの準備 (重複だらけの10万件を想定したモック) —
Dim rawData(9)
rawData(0) = “Banana”
rawData(1) = “Apple”
rawData(2) = “Orange”
rawData(3) = “Apple” ‘ 重複
rawData(4) = “Banana” ‘ 重複
rawData(5) = “Grape”
rawData(6) = “Orange” ‘ 重複
rawData(7) = “Melon”
rawData(8) = “Apple” ‘ 重複
rawData(9) = “Peach”

WScript.Echo “— 処理前 (件数: ” & UBound(rawData) + 1 & “) —”

‘ — 高速ユニーク化の実行 —
Dim uniqueData
uniqueData = GetUniqueArrayFast(rawData)

WScript.Echo “— 処理後 (ユニーク件数: ” & UBound(uniqueData) + 1 & “) —”

Dim i
For i = 0 To UBound(uniqueData)
WScript.Echo “[” & i & “] ” & uniqueData(i)
Next
End Sub

‘ ==============================================================================
‘ 関数名: GetUniqueArrayFast
‘ 概要: Dictionaryを使わず、配列のソートと隣接比較によって重複を除去する
‘ ==============================================================================
Function GetUniqueArrayFast(ByVal targetArray)
Dim i, j
Dim ub
ub = UBound(targetArray)

‘ 要素が空、または1件以下の場合はそのまま返す
If ub < 1 Then GetUniqueArrayFast = targetArray Exit Function End If ' ステップ1: 配列をソートする(ここでは簡潔さのためバブルソートの変形、 ' 実務では高速なクイックソート関数に置き換えてください) Call SortArray(targetArray) ' ステップ2: ソート済み配列から重複を排除する(インプレースに近い省メモリ走査) ' 最大でも元のサイズを超えないため、一時的に同じサイズの配列を用意 Dim tempArray() ReDim tempArray(ub) Dim writeIndex writeIndex = 0 tempArray(0) = targetArray(0) For i = 1 To ub ' 直前に書き込んだ値と異なる場合のみ採用(昇順なので重複は必ず隣り合う) If targetArray(i) <> tempArray(writeIndex) Then
writeIndex = writeIndex + 1
tempArray(writeIndex) = targetArray(i)
End If
Next

‘ ステップ3: 実際に書き込んだサイズに配列を縮小(ReDim Preserve)
ReDim Preserve tempArray(writeIndex)

GetUniqueArrayFast = tempArray
End Function

‘ ==============================================================================
‘ 簡易ソート関数(※実務ではデータ量に応じたソートアルゴリズムを選択してください)
‘ ==============================================================================
Sub SortArray(arr)
Dim i, j, temp
Dim ub
ub = UBound(arr)

For i = 0 To ub – 1
For j = i + 1 To ub
If arr(i) > arr(j) Then
temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
End If
Next
Next
End Sub

4. コードの解説と「エンジニアの知見」

このコードのどこが優れているのか、アーキテクトの視点から解説します。

1. 昇順ソートによる「隣接比較」の魔法

データをソート(並び替え)すると、同じ値は必ず隣同士に並びます。
つまり、全件同士を総当たりで比較するような愚かな真似($O(N^2)$ の計算量)をしなくても、「一個前の要素と違うかどうか」を上から順番にチェックしていくだけ($O(N \log N + N)$ の計算量)で、完璧に重複を排除できるのです。これがこのアルゴリズムの核心です。

2. `Dictionary` を排除したことによるメモリの安定性

`Scripting.Dictionary` は内部でハッシュキーを生成するため、VBScriptが動作するWindowsのヒープメモリを細かく消費し続けます。一方、今回の手法ではVBScriptのネイティブ配列(`ReDim`)しか使いません。メモリの連続領域を効率よく使うため、GC(ガベージコレクション)の負担も最小限に抑えられます。

5. 陥りやすいエラーと注意点

現場でこのコードを応用する際、以下の罠に気をつけてください。

  • 大文字・小文字の区別問題

VBScriptの標準の比較演算子(`=`)は、デフォルトでは大文字・小文字を区別しない場合があります(環境依存やデータ型による)。厳密に区別したい、あるいは無視したい場合は、`StrComp` 関数を導入して比較ロジックを制御してください。

  • NULLや空値(Empty)の混入

データの中に `Null` や `Empty` が混ざっていると、比較演算子でエラー(不適合)が発生します。事前にデータクレンジングを行うか、比較前に型チェックを入れるのがプロの技です。

まとめ

いかがでしたか?今回は `Scripting.Dictionary` に頼らず、純粋な配列操作とソートを組み合わせた【低メモリ型重複排除アルゴリズム】を解説しました。

「ライブラリや便利なオブジェクトがあるから、中身を知らなくてもいいや」ではなく、「裏側で何が起きているのかを知り、リソースを極限まで最適化する」というアプローチこそが、私たちエンジニアの醍醐味です。

ここをクリアすれば、あなたのVBScriptスキルはもう初級者ではありません。ぜひ、日々の業務自動化スクリプトに取り入れて、その爆速っぷりを体感してみてくださいね!

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