vba【配列Aから配列Bの要素を取り除く関数（difference , 差集合）】

差集合を求める必要があって、ネットで検索してみたのですが、どうも配列の二重ループをするようないけてないプログラムしか見つからなかったので自前で実装しました。

```vb
Public Function ArrayDifference(ByVal Heystack As Variant, ByVal Removes As Variant) As Variant
    Dim TmpHeystack As Variant
    TmpHeystack = Heystack
    Dim TmpRemoves As Variant
    TmpRemoves = Removes
    
    Call merge_sort(TmpHeystack)
    Call merge_sort(TmpRemoves)
    
    Dim ret() As Variant
    Dim i As Long
    Dim n As Long
    Do While n <= UBound(Removes) And i <= UBound(Heystack)
        If TmpHeystack(i) < TmpRemoves(n) Then
            ReDim Preserve ret(niceUbound(ret) + 1)
            ret(UBound(ret)) = TmpHeystack(i)
            i = i + 1
        ElseIf TmpHeystack(i) > TmpRemoves(n) Then
            n = n + 1
        Else
            i = i + 1
            n = n + 1
        End If
    Loop
    Dim rest As Long
    For rest = i To UBound(TmpHeystack)
        ReDim Preserve ret(niceUbound(ret) + 1)
        ret(UBound(ret)) = TmpHeystack(rest)
    Next
    ArrayDifference = ret
End Function
Public Function niceUbound(ByVal arr As Variant, Optional Dimension As Integer = 1) As Long
On Error GoTo handler
    niceUbound = UBound(arr, Dimension)
    Exit Function
handler:
    If Err.Number = 9 Then
        niceUbound = -1
    Else
        MsgBox "niceUbound関数に配列ではない変数が渡されました。処理を中止します"
        End
    End If
End Function
Public Sub merge_sort(ByRef arr As Variant)
    Dim irekae As Variant
    Dim i As Long
    ReDim irekae(LBound(arr) To UBound(arr))
    Dim tmp1() As Variant
    ReDim tmp1(LBound(arr, 1) To UBound(arr, 1))
    For i = LBound(arr) To UBound(arr) Step 2
        If i + 1 > UBound(arr) Then
            irekae(i) = arr(i)
            Exit For
        End If
        If arr(i + 1) < arr(i) Then
            irekae(i) = arr(i + 1)
            irekae(i + 1) = arr(i)
        Else
            irekae(i) = arr(i)
            irekae(i + 1) = arr(i + 1)
        End If
    Next
    Dim st1 As Long
    Dim en1 As Long
    Dim st2 As Long
    Dim en2 As Long
    Dim n As Long
    i = 1
    Do While i * 2 <= UBound(arr)
        i = i * 2
        n = 0
        Do While en2 + i - 1 < UBound(arr)
            n = n + 1
            st1 = i * 2 * (n - 1) + LBound(arr)
            en1 = i * 2 * (n - 1) + i - 1 + LBound(arr)
            st2 = en1 + 1
            en2 = IIf(st2 + i - 1 >= UBound(arr), UBound(arr), st2 + i - 1)
            Call merge(irekae, tmp1, st1, en1, st2, en2)
        Loop
        en2 = 0
    Loop
    arr = irekae
End Sub
Private Sub merge(ByRef irekae As Variant, _
ByRef tmpArr() As Variant, _
ByVal st1 As Long, _
ByVal en1 As Long, _
ByVal st2 As Long, _
ByVal en2 As Long)
    Dim j As Long
    Dim n As Long
    Dim i As Long
    For i = st1 To en2
        tmpArr(i) = irekae(i)
    Next
    j = st1
    n = st2
    Do While (j < en1 + 1 Or n < en2 + 1)
        If n >= en2 + 1 Then
            irekae(j + n - st2) = tmpArr(j)
            j = j + 1
        ElseIf j < en1 + 1 And tmpArr(j) <= tmpArr(n) Then
            irekae(j + n - st2) = tmpArr(j)
            j = j + 1
        Else
            irekae(j + n - st2) = tmpArr(n)
            n = n + 1
        End If
    Loop
End Sub
```

呼び出し方

```vb
Sub diftest()
    Dim a As Variant
    Dim b As Variant
    Dim ret As Variant
    a = Array(3, 1, 4, 2, 6)
    b = Array(2, 4, 5)
    ret = ArrayDifference(a, b) '->array(1,3,6)
End Sub
```
返される配列が昇順で並び変わっているので注意が必要です。
ソートの関数もついてるので長ったらしいのですが、配列が大きい時は特に高速に動きます。
※ソートの関数は著作権の関係で面倒なので自前で作ったものを使っています。