Thank you for all of the help. I have successfully populated and reviewed the contents of my array. Now I am having trouble calling the specific instances (string values) within the array in a function I wrote to compare cells in the sheet to the values in the array....
I am getting the "subscript out of range" inside of my function in my strcomp(). I have checked and the right value is being passed via vCompare.
Arrays are so finicky!
Here is the updated code:
Sub searchTrucks()
Dim lastRow As Long
Dim EndRow As Long
Dim showAll As Boolean
Dim BeginRow As Long
Dim RowCnt As Long
Dim chckTech As Long
Dim chckReg As Long
Dim chckSite As Long
Dim chckUnum As Long
Dim chckType As Long
Dim chckAge As Long
Dim chckDt As Long
Dim chckCap As Long
Dim i As Integer
Dim aRan As Range
Dim bRan As Range
Dim cRan As Range
Dim rrRan As Range
Dim rmRan As Range
Dim marray() As Variant
marray = WorksheetFunction.Transpose(Worksheets("Calculations").Range("F2:K2"))
Dim vCompare As String
Dim x As Long
Dim y As Long
y = 2
x = 1
i = 1
lastRow = Application.CountA(Sheets("Trucks").Range("C:C"))
BeginRow = 6
EndRow = lastRow + 4
chckSite = 3
chckUnum = 4
chckType = 5
chckAge = 7
chckDt = 10
chckCap = 11
Debug.Print lastRow
For i = 1 To 8
If IsEmpty(Sheets("Trucks").Cells(2, i).Value) Then
showAll = True
Else
showAll = False
Exit For
End If
Next i
Debug.Print showAll
If showAll = False Then
For RowCnt = BeginRow To EndRow
If Not IsEmpty(Sheets("Trucks").Cells(2, 3).Value) And IsEmpty(Sheets("Trucks").Cells(2, 4).Value) Then
For y = 2 To 6
If Sheets("Trucks").Cells(2, 3).Value = Sheets("Calculations").Cells(y, 5).Value Then
vCompare = Sheets("Trucks").Cells(RowCnt, chckSite).Value
If IsInArray(vCompare, marray) = -1 Then
Cells(RowCnt, chckSite).EntireRow.Hidden = True
End If
End If
Next
Stop
End If
If Not IsEmpty(Sheets("Trucks").Cells(2, 4).Value) And Sheets("Trucks").Cells(RowCnt, chckSite).Value <> Sheets("Trucks").Cells(2, 4).Value Then
Cells(RowCnt, chckSite).EntireRow.Hidden = True
ElseIf Not IsEmpty(Sheets("Trucks").Cells(2, 5).Value) And Sheets("Trucks").Cells(RowCnt, chckUnum).Value <> Sheets("Trucks").Cells(2, 5).Value Then
Cells(RowCnt, chckUnum).EntireRow.Hidden = True
ElseIf Not IsEmpty(Sheets("Trucks").Cells(2, 6).Value) And Sheets("Trucks").Cells(RowCnt, chckType).Value <> Sheets("Trucks").Cells(2, 6).Value Then
Cells(RowCnt, chckType).EntireRow.Hidden = True
ElseIf Not IsEmpty(Sheets("Trucks").Cells(2, 7).Value) And Sheets("Trucks").Cells(RowCnt, chckAge).Value < Sheets("Trucks").Cells(2, 7).Value Then
Cells(RowCnt, chckAge).EntireRow.Hidden = True
ElseIf Not IsEmpty(Sheets("Trucks").Cells(2, 9).Value) And Sheets("Trucks").Cells(RowCnt, chckDt).Value < Sheets("Trucks").Cells(2, 9).Value Then
Cells(RowCnt, chckDt).EntireRow.Hidden = True
ElseIf Not IsEmpty(Sheets("Trucks").Cells(2, 10).Value) And Sheets("Trucks").Cells(RowCnt, chckCap).Value < Sheets("Trucks").Cells(2, 10).Value Then
Cells(RowCnt, chckCap).EntireRow.Hidden = True
End If
Next RowCnt
Else
Sheets("Trucks").Cells.EntireRow.Hidden = False
End If
Here is my function code:
Function IsInArray(stringToBeFound As String, arr As Variant) As Long
Dim i As Long
' default return value if value not found in array
IsInArray = -1
Debug.Print stringToBeFound
For i = LBound(arr) To UBound(arr)
If StrComp(stringToBeFound, arr(i), vbTextCompare) = 0 Then
IsInArray = i
Exit For
End If
Next i
End Function
To populate your arrays you can do this
Dim aArray As Variant
aArray = WorksheetFunction.Transpose(Worksheets("Calculations").Range("F2:K2"))
And similarly for all the rest of your arrays.
You cannot use debug.print on arrays. Instead, in your VBA editor right-click on the variable name (aArray) and select "Add watch". Your variable will appear in the "Watches" window. Now add a break-point just after you (correctly) populate aArray in the code and run your code. It will stop at the break-point and you can now go into the "Watches" window and expand the aArray variable. You will see the contents of the array here.
Regarding the use of the Array function, see here - a comma delimited list of items is required. It is often used to do quick-and-dirty creation of variant arrays, often for static data consisting of small lists. For instance, things like Array("Jan", "Feb", "Mar",...,"Dec")... stuff like that.
You generally do not need to call this constructor explicitly when using arrays. For simple non-Variant data types an array of type X is defined like so:
dim an_X_array(10) as X
This defines an_X_array to be an array of 10 items that each have type X
Compare this to a simple variable defined to be of type X
dim an_X as X
With regards to your second problem - it is being caused by the array you create from your range (worksheet data) being constructed as a 2-dimensional array. You can either work with 2-dimensional arrays, and change your formulas, or use the below helper function to create a 1-dimensional array from your worksheet data. Here is the function to create a proper 1-dimensional array from any worksheet range (just copy-paste it somewhere in your code module):
Public Function RngToArray(ByRef InputRange As Range) As Variant
Dim A As Variant
Dim rr As Range
Dim i As Long
ReDim A(InputRange.Cells.Count)
i = LBound(A)
For Each rr In InputRange
A(i) = rr.Value
i = i + 1
Next
ReDim Preserve A(i - 1)
RngToArray = A
End Function
And for your example you then need to replace just one line of your code:
change
marray = WorksheetFunction.Transpose(Worksheets("Calculations").Range("F2:K2"))
to
marray = RngToArray(Worksheets("Calculations").Range("F2:K2"))
the way you populate your array , you will get a 2 dimensional array, so i modified your source code to test if your value is in the array :
Function IsInArray( Byval stringToBeFound As String, Byref arr As Variant) As Long
Dim i As Long 'i is the columns variable
Dim J& 'j is the rows variable
' default return value if value not found in array
IsInArray = -1
Debug.Print stringToBeFound
For i = LBound(arr,2) To UBound(arr,2) 'the ,2 is to say the 2nd dimension (same order of dimensions as if you'd use the cells function)
For j = LBound(arr,1) To UBound(arr,1)
If stringToBeFound = arr(j,i) Then 'simple test of strings
IsInArray = i 'will give the column as answer
Exit Function 'Exit For
End If
Next i
End Function
Related
I have 2 arrays taken from 2 ranges in a sheet. I'm trying to create a third array that contains only the values contained in array 1 that are missing in array 2 (I found this code online).
Array 2´s size will vary and depends on this code:
Dim iListaIncompleta() As Variant
Dim iCountLI As Long
Dim iElementLI As Long
iCountLI = Range("B1").End(xlDown).Row
ReDim iListaIncompleta(iCountLI)
For iElementLI = 1 To iCountLI
iListaIncompleta(iElementLI - 1) = Cells(iElementLI, 2).Value
Next iElementLI
and Array 1's size is always from A1:A7, and I use this code to create it:
Dim iListaCompleta() As Variant
Dim iElementLC As Long
iListaCompleta = Range("A1:A7")
This is the original code I found online to extract missing values:
Dim v1 As Variant, v2 As Variant, v3 As Variant
Dim coll As Collection
Dim i As Long
'Original Arrays from the code:
v1 = Array("Bob", "Alice", "Thor", "Anna") 'Complete list
v2 = Array("Bob", "Thor") 'Incomplete list
Set coll = New Collection
For i = LBound(v1) To UBound(v1)
If v1(i) <> 0 Then
coll.Add v1(i), v1(i) 'Does not add value if it's 0
End If
Next i
For i = LBound(v2) To UBound(v2)
On Error Resume Next
coll.Add v2(i), v2(i)
If Err.Number <> 0 Then
coll.Remove v2(i)
End If
If coll.Exists(v2(i)) Then
coll.Remove v2(i)
End If
On Error GoTo 0
Next i
ReDim v3(LBound(v1) To (coll.Count) - 1)
For i = LBound(v3) To UBound(v3)
v3(i) = coll(i + 1) 'Collections are 1-based
Debug.Print v3(i)
Next i
End Sub
However, this code has arrays defined like this:
v1 = Array("Bob", "Alice", "Thor", "Anna")
And the actual arrays I wanna use are defined differently (as you can see in the first two pieces of code). When I try to run the code with them, it displays
Error 9: Subscript out of range.
The code works well as it originally is, but when I try to use MY arrays, it's when I get this error.
Obviously, I've tried it changing the names of the variables (v1 and v2) to my own 2 arrays (iListaCompleta and iListaIncompleta), and still doesn't work.
Any ideas??
Thank you in advance!
Here's a function that can be used to compare arrays of any dimension size to pull out differences and put only the differences in a one-dimensional array:
Public Function ArrayDifference(ByVal arg_Array1 As Variant, ByVal arg_array2 As Variant) As Variant
If Not IsArray(arg_Array1) Or Not IsArray(arg_array2) Then Exit Function 'Arguments provided were not arrays
Dim vElement As Variant
Dim hDifference As Object: Set hDifference = CreateObject("Scripting.Dictionary")
For Each vElement In arg_Array1
If Not hDifference.exists(vElement) Then hDifference.Add vElement, vElement
Next vElement
For Each vElement In arg_array2
If hDifference.exists(vElement) Then
hDifference.Remove vElement
Else
hDifference.Add vElement, vElement
End If
Next vElement
ArrayDifference = hDifference.Keys
End Function
Here's how you would call the function to compare two different arrays. It also includes how to populate the initial arrays using your provided setup:
Sub arrays()
Dim ws As Worksheet: Set ws = ActiveWorkbook.ActiveSheet
Dim rList1 As Range: Set rList1 = ws.Range("A1", ws.Cells(ws.Rows.Count, "A").End(xlUp))
Dim rList2 As Range: Set rList2 = ws.Range("B1", ws.Cells(ws.Rows.Count, "B").End(xlUp))
Dim aList1 As Variant
If rList1.Cells.Count = 1 Then
ReDim aList1(1 To 1, 1 To 1)
aList1(1, 1) = rList1.Value
Else
aList1 = rList1.Value
End If
Dim aList2 As Variant
If rList2.Cells.Count = 1 Then
ReDim aList2(1 To 1, 1 To 1)
aList2(1, 1) = rList2.Value
Else
aList2 = rList2.Value
End If
Dim aList3 As Variant
aList3 = ArrayDifference(aList1, aList2)
MsgBox Join(aList3, Chr(10))
End Sub
I'm trying to create an array with only unique values (Signal Names). For example my spreadsheet looks like this
Voltage
Voltage
Voltage
Current
Current
Current
etc....
I've got 32 signals however, I want this to work even if I don't know I have 32 signals explicitly i.e. 17 signals.
Signals("Voltage", "Current", "Etc....")
IN THE CODE BELOW
I realize I'm trying to ReDim an array within a loop and that's the problem. I'm just not able to think of another way of doing this. I would prefer to keep it as an array problem and not a dictionary or collection problem for now.
Public Sub Signals()
Dim myArray() As Variant
Dim Signals() As Variant
Dim element As Variant
Dim intA As Integer
WsName = ActiveSheet.Name
intRows = Sheets(WsName).Range("B2", Sheets(WsName).Range("B" & Sheets(WsName).Rows.Count).End(xlUp)).Rows.Count
intRows = intRows + 1
ReDim Signals(1)
Signals(1) = Sheets(WsName).Cells(4, 2).Value
For intA = 4 To intRows
For Each element In Signals()
If element <> Sheets(WsName).Cells(intA, 2) Then
ReDim Signals(UBound(Signals) + 1) 'This throws the error
Signals(UBound(Signals)) = Sheets(WsName).Cells(intA, 2).Value
End If
Next element
Next
End Sub
How the code doesn't work - RunTime Error '10' Array is temporarily fixed or locked.
I posted a solution to this issue using arrays in a similar question a couple days ago - using column B for your case, this would do the trick.
Aside from this solution, you have several problems in your current code - you're testing against each individual element in your current array without checking them all first, you're not using ReDim Preserve, and you need (0 to 0), not just a single (0) or (1). You're also naming your subroutine "Signals" while attempting to declare a variable "Signals" in the subroutine as well... That'll cause all kinds of issues.
Sub Test()
Dim list() As Variant
Dim inlist As Boolean
Dim n As Long, i As Long, j As Long, endrow As Long, colnum As Long
ReDim list(0 To 0)
inlist = False
j = 0
colnum = 2 'Column B in this case
endrow = Cells(Rows.Count, colnum).End(xlUp).Row
For n = 1 To endrow
For i = 0 To UBound(list)
If list(i) = Cells(n, colnum).Value Then
inlist = True
Exit For
End If
Next i
If inlist = False Then
ReDim Preserve list(0 To j)
list(j) = Cells(n, colnum).Value
j = j + 1
End If
inlist = False
Next n
For i = 0 To UBound(list)
Debug.Print list(i)
Next i
End Sub
Even simpler solution thanks to #user10829321's suggestions:
Sub Test()
Dim list() As Variant
Dim n As Long, i As Long, j As Long, endrow As Long, colnum As Long
ReDim list(0 To 0)
j = 0
colnum = 2 'Column B in this case
endrow = Cells(Rows.Count, colnum).End(xlUp).Row
For n = 1 To endrow
If IsError(Application.Match(Cells(n, colnum).Value, list, 0)) Then
ReDim Preserve list(0 To j)
list(j) = Cells(n, colnum).Value
j = j + 1
End If
Next n
For i = 0 To UBound(list)
Debug.Print list(i)
Next i
End Sub
An optional, if perhaps unwanted, solution using a scripting dictionary to give an array.
Public Function Signals(ByRef this_worksheet_range As excel.Range) As Variant()
Dim myArray() As Variant
Dim element As Variant
Dim interim_dic As Scripting.Dictionary
myArray = this_worksheet_range.values2
Set interim_dic = New Scripting.Dictionary
For Each element In myArray
If Not interim_dic.Exists(element) Then
interim_dic.Add Key:=element, Item:=element
End If
Next
Signals = interim_dic.Items
End Function
Hopefully i've phrased that right...
I came across something online stating that copy and pasting wastes precious time. It's better to assign values more directly, without using excel functions.
I found a section in a VBA book explaining how to store a range in a 2D array.
Now what if I wanted to copy and paste a range from a dynamic number of worksheets into another one main sheet with this method?
In my head, I imagine stacking more and more values into an array, then dumping the array where I'd like it to go, into a range whose size is defined by the dimensions of the big array.
In practice, all I have managed to create is something like the below, performing the same simple action for each worksheet in turn.
Is it possible to do this better? That runs faster? Help a brother out!
Sub arrayCopyPaste()
Dim Obj As Range
Dim Data As Variant
Dim ws As Worksheet
Dim sheetCount As Integer
Dim LR As Integer
sheetCount = Sheets.Count
Set ws = Sheets.Add
ws.Move After:=Worksheets(Worksheets.Count)
For i = 1 To sheetCount
Data = Sheets(i).Range("A1:B9")
LR = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set Obj = ws.Range("A" & LR)
Set Obj = Obj.Resize(UBound(Data, 1), UBound(Data, 2))
Obj.Value = Data
Next i
End Sub
With just about any code I use, I like to make a call to this routine I made:
Sub SpeedupCode(Optional ByVal Val As Boolean = True)
With Application
If Val = True Then
.ScreenUpdating = False
.Calculation = xlCalculationManual
Else
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End If
End With
End Sub
So, in your code you would simply use it as follows:
Sub arrayCopyPaste()
Dim Obj As Range
Dim Data As Variant
Dim ws As Worksheet
Dim sheetCount As Integer
Dim LR As Integer
SpeedupCode
sheetCount = Sheets.Count
Set ws = Sheets.Add
ws.Move After:=Worksheets(Worksheets.Count)
For i = 1 To sheetCount
Data = Sheets(i).Range("A1:B9")
LR = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Set Obj = ws.Range("A" & LR)
Set Obj = Obj.Resize(UBound(Data, 1), UBound(Data, 2))
Obj.Value = Data
Next i
SpeedupCode False
End Sub
While this does not necessarily optimize your code, it can significantly improve the performance on every project that you do. In the event that your code requires a newly calculated variable in your worksheet, you can always use Application.Calculate before you grab that variable, but generally, it shouldn't be needed.
I'd be inclined to use your current approach and just boil it down a bit.
Sub arrayCopyPaste()
Dim ws As Worksheet
Set ws = Sheets.Add(After:=Worksheets(Worksheets.Count))
For i = 1 To Sheets.Count - 1
With Sheets(i).Range("A1:B9")
ws.Cells(ws.Rows.Count, 1).End(xlUp).Offset(1, 0).Resize( _
.Rows.Count, .Columns.Count).Value = .Value
End With
Next i
End Sub
This version is slightly more efficient due to writing the results all at once, though you probably won't notice much of a difference unless you're working with very large ranges.
Sub test()
'Same as original: final array is 2 columns wide, (3 * number of sheets) rows long
Call mergeRangeValues("A1:B3", "Results", True)
'Alternate version: final array is 3 rows long, (2 * number of sheets) columns wide
'Call mergeRangeValues("A1:B3", "Results", False)
End Sub
Sub mergeRangeValues(rngString As String, newWSName As String, stackRows As Boolean)
'Merges the same range (rngString) from all sheets in a workbook
'Adds them to a new worksheet (newWSName)
'If stackRows = True, values are stacked vertically
'If stackRows = False, values are stacked horizontally
Dim sheetCount As Long
Dim newWS As Worksheet
sheetCount = ThisWorkbook.Sheets.Count
Set newWS = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(sheetCount))
newWS.Name = newWSName
Dim numCols As Long
Dim numRows As Long
numCols = newWS.Range(rngString).Columns.Count * IIf(stackRows, 1, sheetCount)
numRows = newWS.Range(rngString).Rows.Count * IIf(stackRows, sheetCount, 1)
ReDim resultsArr(1 To numRows, 1 To numCols) As Variant
'''Longer version:
'If stackRows Then
'numCols = newWS.Range(rngString).Columns.Count
'numRows = newWS.Range(rngString).Rows.Count * sheetCount
'Else
'numCols = newWS.Range(rngString).Columns.Count * sheetCount
'numRows = newWS.Range(rngString).Rows.Count
'End If
'''ie "If you want to stack the results vertically, make the array really long"
'''or "If you want to stack the results horizontally, make the array really wide"
Dim i As Long
For i = 0 To sheetCount - 1
Dim tempArr As Variant
tempArr = ThisWorkbook.Sheets(i + 1).Range(rngString).Value
Dim j As Long
Dim k As Long
If stackRows Then
For j = LBound(tempArr, 1) To UBound(tempArr, 1)
For k = LBound(tempArr, 2) To UBound(tempArr, 2)
resultsArr(j + i * (numRows / sheetCount), k) = tempArr(j, k)
Next
Next
Else
For j = LBound(tempArr, 1) To UBound(tempArr, 1)
For k = LBound(tempArr, 2) To UBound(tempArr, 2)
resultsArr(j, k + i * (numCols / sheetCount)) = tempArr(j, k)
Next
Next
End If
Next
With newWS
.Range(.Cells(1, 1), .Cells(numRows, numCols)).Value = resultsArr
End With
End Sub
I have a module in a worksheet that is supposed to pass an array to another sub in the same module. So far, I've noticed that the variable N which is being used to to pull each individual array element always says 0 in the watch window, how do I get my elements out of the array? Here is the code:
Option Explicit
Sub CreateReports()
Dim numRows As Integer
Dim numCount As Integer
Dim category As String
Dim size As Integer
Dim sizeCount As Integer
Dim departmentNums() As Integer
With Sheets("GM Alignment")
numRows = Application.WorksheetFunction.CountA(Range("A2:A1048576"))
.Range("A2").Select
Do While numCount < numRows
category = ActiveCell.Value
size = Range(ActiveCell.Offset(0, 1), ActiveCell.Offset(0, 1).End(xlToRight)).Columns.Count - 1
If size > 7 Then
size = 0
End If
ReDim departmentNums(size)
.Cells(ActiveCell.Row, 1).Select
For sizeCount = 0 To size
ActiveCell.Offset(0, 1).Select
departmentNums(sizeCount) = ActiveCell.Value
Next sizeCount
.Cells(ActiveCell.Row, 1).Select
GenerateReports Arr:=departmentNums, Sheet:=category
ActiveCell.Offset(1, 0).Select
numCount = numCount + 1
Loop
End With
End Sub
Sub GenerateReports(ByRef Arr() As Integer, Sheet As String)
Dim N As Integer
For N = LBound(Arr) To UBound(Arr)
Dim Lastrow As Long
With Sheets("DATA")
If .Range("I:I").Find(N, , xlValues, xlWhole, , , False) Is Nothing Then
MsgBox "No " + Sheet + " rows found. ", , "No Rows Copied": Exit Sub
Else
Application.ScreenUpdating = False
Lastrow = .Range("K" & Rows.Count).End(xlUp).Row
.Range("K1:K" & Lastrow).AutoFilter Field:=1, Criteria1:=N
.Range("K2:K" & Lastrow).SpecialCells(xlCellTypeVisible).EntireRow.Copy
Sheets(Sheet).Range("A2").PasteSpecial xlPasteValues, xlPasteSpecialOperationNone, False, False
.AutoFilterMode = False
'Position on cell A3
With Application
.CutCopyMode = False
.Goto Sheets("DATA").Range("A2")
.ScreenUpdating = True
End With
MsgBox "All matching data has been copied.", , "Copy Complete"
End If
End With
Next N
End Sub
Thanks!
Not really looking too far into your code but I noticed that at no point do you read from or assign to Arr in GenerateReports. I believe you are misunderstanding how Arrays behave.
Dim index as Integer
For index = LBound(Arr) To UBound(Arr)
Debug.Print "Index: "; index; " Value: " Arr(index)
Next N
LBound(Arr) and Ubound(Arr) return the lowest and highest index of Arr not the values. In order to access the values contained in Arr use Arr(index).
If you don't care about the index you can use
Dim element as Integer
For each element in Arr
debug.Print element
Next n
It is recommended to use this method whenever possible, as it allows for other sequence's to be used such as Collection. However, it is not always possible such as when you are iterating over multiple sequences or parts of a sequence.
Here is a basic example:
Sub PrintOneToTen()
Dim xs(1 to 10) as Integer
FillArray xs
Dim x as Integer
For Each x In xs
Debug.Print x
Next x
' or just Debug.Print Join(xs, vbNewLine)
End Sub
Sub FillArray(ByRef xs() As Integer)
Dim i as Integer
For i = LBound(xs) To Ubound(xs)
xs(i) = i
Next i
End Sub
Documentation for VB.NET Arrays is listed here. Note VB.NET is not VBA, but as far as arrays are concerned 99% of the info there should be the same in VBA. I would post the VBA documentation but it's buried deep in Microsofts' database and I don't believe they care about it.
how do i filter an array using another array vb6
Edit
given an array A, remove all elements in array B from array A
In that case, I'd just sort one array, then iterate through the second, deleting things from the first array if they are found. This algorithm seems to take O(n lg n) and does what you want it to do.
Assuming they are integer arrays:
Dim FilteredArray() As Integer
Dim X as Long
Dim Y as Long
Dim Z as Long
Dim bDupe as Boolean
Z = -1
For X = 0 to UBound(A)
bDupe = False
For Y = 0 to UBound(B)
If A(X) = B(Y) Then
bDupe = True
Exit For
End If
Next
If Not bDupe Then
Z = Z + 1
ReDim Preserve FilteredArray(Z)
FilteredArray(Z) = A(X)
End If
Next
Try something like this
Option Explicit
Private Sub Form_Load()
Dim vElem As Variant
For Each vElem In SubstractArray(Array("aa", "b", "test"), Array("c", "aa", "test"))
Debug.Print vElem
Next
End Sub
Private Function SubstractArray(arrSrc As Variant, arrBy As Variant) As Variant
Dim cIndex As Collection
Dim vElem As Variant
Dim vRetVal As Variant
Dim lIdx As Long
If UBound(arrSrc) < LBound(arrSrc) Then
Exit Function
End If
'--- build index collection
Set cIndex = New Collection
For Each vElem In arrBy
cIndex.Add vElem, "#" & vElem
Next
'--- allocate output array
lIdx = LBound(arrSrc)
ReDim vRetVal(lIdx To UBound(arrSrc)) As Variant
'--- iterate source and seek in index
For Each vElem In arrSrc
On Error Resume Next
IsObject cIndex("#" & vElem)
If Err.Number <> 0 Then
vRetVal(lIdx) = vElem
lIdx = lIdx + 1
End If
On Error GoTo 0
Next
'--- shrink output array
If lIdx = LBound(vRetVal) Then
vRetVal = Split(vbNullString)
Else
ReDim Preserve vRetVal(0 To lIdx - 1) As Variant
End If
SubstractArray = vRetVal
End Function
i have found the answer myself, thanks for all who contributed
Function FilterArray(ByVal Source As String, ByVal Search As String, Optional _
ByVal Keep As Boolean = True) As String
Dim i As Long
Dim SearchArray() As String
Dim iSearchLower As Long
Dim iSearchUpper As Long
If LenB(Source) <> 0 And LenB(Search) <> 0 Then
SearchArray = Split(Search, " ")
Else
FilterArray = Source
Exit Function
End If
iSearchLower = LBound(SearchArray)
iSearchUpper = UBound(SearchArray)
For i = iSearchLower To iSearchUpper
DoEvents
Source = Join(Filter(Split(Source, " "), SearchArray(i), Keep, _
vbTextCompare), " ")
Next i
FilterArray = Source
End Function