Sorting order check using VBScript - vba

Suppose I have an array arr1 which contains date values as below :
Arr1(50)=("9/3/2012 4:57:02 AM","22/3/2012 5:57:02 AM","9/5/2012 8:57:02 AM","9/3/2011 4:57:02 AM")
Edit
Dim CellCount
Dim Arr(10)
CellCount=0
Do Untill arr(CellCount)="" And Ubound(Arr)>9
If CStr(arr(CellCount) < arr(CellCount+1)) Then
MsgBox(arr(CellCount)&"is good")
Else
MsgBox(arr(CellCount +1)&"is bad")
Exit Do
End if
CellCount=CellCount+1
Loop
Now, Is there any direct way,without using any Looping technique,to find out if the Arr1() has the date values ascending or not?
Thanks,

He is answering your question. You asked, is there a way to find if sorted without a loop?
He said no. Your eyes, my eyes can see if it's sorted or not. But how do you expect Excel/computer to do so without going through collections elements?
When data is in an array, you need to loop through its elements. Hope it's clear.
So the best I could say,
split it by the comma delimiter into a variant if required. But not necessary since Array() can put elements into a 1D variant
dump it into a Range using Transpose
use Sheet sorting method/function to sort the range as a whole
---> so you know it's now sorted.
then traspose back into a variant array
Some code snippet to get you going on this direction:
Option Explicit
Sub omgArraySort()
Dim inputArray As Variant
Dim outputArray As Variant
Dim upperB as Long
inputArray = Array("9/3/2012 4:57:02 AM","22/3/2012 5:57:02 AM", _
"9/5/2012 8:57:02 AM","9/3/2011 4:57:02 AM")
'-- sorted array
outputArray = sortRange(inputArray)
upperB = UBound(iArray, 1) '-- for 1D array you may also use UBound(iArray)
If (Err.Number <> 0) Then '-- if there's an error, it's erro code is > 0
MsgBox "Dates sorted, not empty"
End If
End Sub
'-- dump into sheet and sort in the sheet and dump back into the array
Function sortRange(ByVal iArray As Variant) As Variant
Dim rngSort as Range
Dim i As Long
Set rngSort = WorkSheets(1).Range("B2")
i = Ubound(iArray,1)
With rngSort.Resize(i)
.Value = WorksheetFunction.Transpose(iArray)
.Sort rngSort, xlDescending, Header:=xlNo
sortRange = .Value
End With
End Function
In terms of to-from sheet to code traffic can slow down your performance when you are handling large amount of data.
And please do know that when someone in SO who has credibility answers (E.g. Ekkehard's answer) you gotta pay some attention. They don't say it for no proper reason.

No, because you have to check the elements upto/until the first counter example (which could be the last one).

Related

VBA: How do I get unique values in a column and insert it into an array?

I have seen multiple codes regarding this topic but I can't seem to understand it.
For instance, if I have a column that records people names, I want to record all unique names into the array.
So if I have a column of names
David
Johnathan
Peter
Peter
Peter
Louis
David
I want to utilize VBA to extract unique names out of the column and place it into an array so when I call the array it would return these results
Array[0] = David
Array[1] = Johnathan
Array[2] = Peter
Array[3] = Louis
Despite a Collection being mentioned and being a possible solution, it is far more efficient to use a Dictionary as it has an Exists method. Then it's just a matter of adding the names to the dictionary if they don't already exist, and then extracting the keys to an array when you're done.
Note that I've made the name comparisons case-sensitive, but you can change that if necessary, to case-insensitive.
Option Explicit
Sub test()
'Extract all of the names into an array
Dim values As Variant
values = Sheet1.Range("Names").Value2 'Value2 is faster than Value
'Add a reference to Microsoft Scripting Runtime
Dim dic As Scripting.Dictionary
Set dic = New Scripting.Dictionary
'Set the comparison mode to case-sensitive
dic.CompareMode = BinaryCompare
Dim valCounter As Long
For valCounter = LBound(values) To UBound(values)
'Check if the name is already in the dictionary
If Not dic.Exists(values(valCounter, 1)) Then
'Add the new name as a key, along with a dummy value of 0
dic.Add values(valCounter, 1), 0
End If
Next valCounter
'Extract the dictionary's keys as a 1D array
Dim result As Variant
result = dic.Keys
End Sub
use Dictionary object and build a Function that returns your array
Function GetUniqeNames(myRng As Range) As Variant
Dim cell As Range
With CreateObject("Scripting.Dictionary") ' instantiate and reference a Dictionary object
For Each cell In myRng ' loop through passed range
.Item(cell.Value2) = 1 ' store current cell name into referenced dictionary keys (duplicates will be overwritten)
Next
GetUniqeNames = .keys ' write referenced dictionary keys into an array
End With
End Function
that you can exploit in your main code as follows
Sub main()
Dim myArray As Variant
With Worksheets("mysheet") ' change "mysheet" to your actual sheet name
myArray = GetUniqeNames(.Range("A1", .Cells(.Rows.Count, 1).End(xlUp))) ' this will take the referenced sheet column A range from row 1 down to last not empty one
End With
End Sub
Is this a VBA question or a question about programming logic? Use a loop on the column with the data. Check each name against the list of existing data items. If it exists in the list, move on the the next name. If it does not exist in the list, add it.
The "list" is a concept, not a concrete tool. It can be a VBA dictionary, if you are comfortable using that. Or it can be a VBA array, which may not perform as fast as a dictionary, but may be more familiar.
Then again, if you add the data to the Excel Data Model, you can use the Distinct aggregation of a pivot table to list out the unique values.
Without more background it's hard to tell if VBA or Data Model is your best approach. Many VBA solutions get created because people are not aware of Excel's capabilities.
You could use Excel functionality like that.
Sub UniqueNames()
Dim vDat As Variant
Dim rg As Range
Dim i As Long
Set rg = Range("A1:A7")
rg.RemoveDuplicates Columns:=Array(1), Header:=xlNo
With ActiveSheet
vDat = WorksheetFunction.Transpose(.Range("A1:" & .Range("A1").End(xlDown).Address))
End With
For i = LBound(vDat) To UBound(vDat)
Debug.Print vDat(i)
Next i
End Sub
Code is based on your example data, i.e. I put your data into column 1. But the code will also alter the table. If you do not want that you have to use other solutions or put the data beforehand in a temporary sheet.
If you dont want to use "Scripting.Dictionary" and your excel does not have Worksheet.unique(...) like mine
Public Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
If UBound(arr) >= 0 Then
IsInArray = Not IsError(Application.Match(stringToBeFound, arr, 0))
Else
IsInArray = False
End If
End Function
Public Function GetUniqueValuesFromColumn(ws As Worksheet, sourceColNum As Long, Optional firstRow As Long = 2) As Variant
Dim val As String
Dim i As Long
Dim arr() As Variant
arr = Array()
For i = firstRow To ws.Cells(Rows.Count, sourceColNum).End(xlUp).Row
val = ws.Cells(i, sourceColNum)
If Not IsInArray(val, arr) Then
ReDim Preserve arr(UBound(arr) + 1)
arr(UBound(arr)) = val
End If
Next i
GetUniqueValuesFromColumn = arr
End Function
Then call it like GetUniqueValuesFromColumn(ThisWorkbook.Worksheets("SomeList"), 1)

Excel VBA: Consolidate the iterating group in "For Each" block

I found the concatenation/appending of two arrays to be a too cumbersome process for my example. But how to iterate through two arrays of sheets using For Each in a single block (how to shorten the following code)?
arr1 = Array("Sheet2", "Sheet3")
arr2 = Array("Sheet5", "Sheet6")
For Each sh In Sheets(arr1)
sh.Visible = True
Next sh
For Each sh In Sheets(arr2)
sh.Visible = True
Next sh
You can always combine your small arrays into a super array. For example:
Sub Klai()
arr1 = Array("Sheet2", "Sheet3")
arr2 = Array("Sheet5", "Sheet6")
arr3 = Array(arr1, arr2)
For Each a In arr3
For Each b In a
MsgBox Sheets(b).Name
Sheets(b).Visible = True
Next b
Next a
End Sub
Just to throw in yet another solution:
Option Explicit
Sub tmpSO()
Dim lngItem As Long
Dim strArray() As String
Dim strOneLongList As String
Dim arr1 As Variant, arr2 As Variant
'Your starting point
arr1 = Array("Sheet2", "Sheet3")
arr2 = Array("Sheet5", "Sheet6")
'Bring all of them together into one long string containing all sheets
strOneLongList = Join(arr1, "/") & "/" & Join(arr2, "/")
MsgBox "This is what strOneLongList currently looks like:" & Chr(10) & Chr(10) & strOneLongList
'Convert the list into a string array with four elements
strArray = Split(Join(arr1, "/") & "/" & Join(arr2, "/"), "/")
For lngItem = 0 To UBound(strArray)
ThisWorkbook.Worksheets(strArray(lngItem)).Visible = True
Next lngItem
End Sub
Explanations:
The Join functions brings all elements of an array together into one long string.
The Split function is very similar to the array function you originally used. Yet, array requires that you list the items in separate strings delimited by commas. Split expects one long string in which one character is chosen to separate all elements.
Caution: Choose the character wisely you are using in a split list to separate all elements in that list and make sure that this character can never be found in any element of the list. I chose the / character because it cannot be used in a name for a sheet. Alternative characters could be \ or * or something very exotic like ChrW(12484).
It is certainly not as simplistic (and thereby possibly "visually appealing") as the other solution provided by #Garys-Student. Yet, this solution avoids the Arrays function and thereby we can bypass the variables of type variant. The same applies to the For Each... loop. It requires also variables of type variant or object. So, I replaced this one too with a For ... Next loop.
I am not sure if I'd use this construction or the other solution. Maybe this answer is better in terms of speed and overhead. Yet, the other solution is certainly faster coded and easier to read. Is that really worth the potential benefit?
Update:
In short, the following sub is a re-write of your original post and does exactly the same. Yet, it does not make use of variant variables (which reduces overhead and is therefore favorable).
Dim lngItem As Long
Dim strArray() As String
strArray = Split("Sheet2/Sheet3/Sheet5/Sheet6", "/")
For lngItem = LBound(strArray) To UBound(strArray)
ThisWorkbook.Worksheets(strArray(lngItem)).Visible = True
Next lngItem

In Excel 2010, how could I remove duplicates and concatenate values within a cell range that includes multiple values cells?

I made a document in Excel 2010 however, the functionality I'm hoping to get from it doesn't seem to be possible (at least not with the default Excel functions) and I don't know enough about VB programming to make my own UDF. (I'm actually using one I found online which does part of what I want, but doesn't meet all of my needs.)
Let me break it down:
I have multiple sheets with groups of fields where users can add numbers (some will be blank, some will contain a single number, some will contain multiple comma-separated numbers)
I have an "Overview" sheet where I want to Concatenate those numbers (and remove any duplicates) within a few different sections (only looking at specific field groups).
I found a ConcatIf UDF that works fairly well for this, however it can't handle non-consecutive cells to concatenate (For example, I want to concatenate and remove duplicates from cells D30, G30, J30 and M30 together) (Here's the UDF:)
Function ConcatIf(ByVal compareRange As Range, ByVal xCriteria As Variant, Optional ByVal stringsRange As Range, _
Optional Delimiter As String, Optional NoDuplicates As Boolean) As String
Dim i As Long, j As Long
With compareRange.Parent
Set compareRange = Application.Intersect(compareRange, Range(.UsedRange, .Range("a1")))
End With
If compareRange Is Nothing Then Exit Function
If stringsRange Is Nothing Then Set stringsRange = compareRange
Set stringsRange = compareRange.Offset(stringsRange.Row - compareRange.Row, _
stringsRange.Column - compareRange.Column)
For i = 1 To compareRange.Rows.Count
For j = 1 To compareRange.Columns.Count
If (Application.CountIf(compareRange.Cells(i, j), xCriteria) = 1) Then
If InStr(ConcatIf, Delimiter & CStr(stringsRange.Cells(i, j))) <> 0 Imp Not (NoDuplicates) Then
ConcatIf = ConcatIf & Delimiter & CStr(stringsRange.Cells(i, j))
End If
End If
Next j
Next i
ConcatIf = mid(ConcatIf, Len(Delimiter) + 1)
End Function
It also can't handle the "multiple numbers in one cell" as separate numbers.
Is there a way to make a Concatenate UDF that "parses" the cells it's looking at to look for duplicates between the multiple numbers cells and the single numbers cells, and then output the result? Preferably allowing it to take a series of non-consecutive cells to work on (across different sheets).
Sorry if the explanation is a bit convoluted, it's my first time asking for this kind of help. :x
Here's an example:
If I have cells with:
2,4,6
2,6
2
4
6
6,8
I'd want to be able to simply get:
2,4,6,8
Right now, instead, I'd get:
2,4,6,2,6,6,8
Try the below. You can adapt it appropriately if you need to change the delimiter etc. I have documented what it is doing and why.
Example formula: =blah(A1:A7,A8,C9) (it can also be called from code)
Example output: 2,4,6,8
Public Function Blah(ParamArray args()) As String
'Declarations
Dim uniqueParts As Collection
Dim area As Range
Dim arg, arr, ele, part
Dim i As Long
'Initialisations
Set uniqueParts = New Collection
'Enumerate through the arguments passed to this function
For Each arg In args
If TypeOf arg Is Range Then 'range so we need to enumerate its .Areas
For Each area In arg.Areas
arr = area.Value 'for large ranges it is greatly quicker to load the data at once rather than enumerating each cell in turn
For Each ele In arr 'enumerate the array
addParts CStr(ele), uniqueParts 'Call our sub to parse the data
Next ele
Next area
ElseIf VarType(arg) > vbArray Then 'an array has been passed in
For Each ele In arg 'enumerate the array
addParts CStr(ele), uniqueParts 'Call our sub to parse the data
Next ele
Else 'assume can be validly converted to a string. If it cannot then it will fail fast (as intended)
addParts CStr(arg), uniqueParts 'Call our sub to parse the data
End If
Next arg
'process our results
If uniqueParts.Count > 0 Then
ReDim arr(0 To uniqueParts.Count - 1)
For i = 1 To uniqueParts.Count
arr(i - 1) = uniqueParts(i)
Next i
'we now have an array of the unique parts, which we glue together using the Join function, and then return it
Blah = Join(arr, ",")
End If
End Function
'Sub to parse the data. In this case the sub splits the string and adds the split elements to a collection, ignoring duplicates
Private Sub addParts(partsString As String, ByRef outputC As Collection)
'ByRef is unecessary but I use it to document that outputC must be instantiated
Dim part
For Each part In Split(partsString, ",")
On Error Resume Next 'existing same key will raise an error, so we skip it and just carry on
outputC.Add part, part
On Error GoTo 0
Next part
End Sub

Create dictionary of lists in vba

I have worked in Python earlier where it is really smooth to have a dictionary of lists (i.e. one key corresponds to a list of stuff). I am struggling to achieve the same in vba. Say I have the following data in an excel sheet:
Flanged_connections 6
Flanged_connections 8
Flanged_connections 10
Instrument Pressure
Instrument Temperature
Instrument Bridle
Instrument Others
Piping 1
Piping 2
Piping 3
Now I want to read the data and store it in a dictionary where the keys are Flanged_connections, Instrument and Piping and the values are the corresponding ones in the second column. I want the data to look like this:
'key' 'values':
'Flanged_connections' '[6 8 10]'
'Instrument' '["Pressure" "Temperature" "Bridle" "Others"]'
'Piping' '[1 2 3]'
and then being able to get the list by doing dict.Item("Piping") with the list [1 2 3] as the result. So I started thinking doing something like:
For Each row In inputRange.Rows
If Not equipmentDictionary.Exists(row.Cells(equipmentCol).Text) Then
equipmentDictionary.Add row.Cells(equipmentCol).Text, <INSERT NEW LIST>
Else
equipmentDictionary.Add row.Cells(equipmentCol).Text, <ADD TO EXISTING LIST>
End If
Next
This seems a bit tedious to do. Is there a better approach to this? I tried searching for using arrays in vba and it seems a bit different than java, c++ and python, with stuft like redim preserve and the likes. Is this the only way to work with arrays in vba?
My solution:
Based on #varocarbas' comment I have created a dictionary of collections. This is the easiest way for my mind to comprehend what's going on, though it might not be the most efficient. The other solutions would probably work as well (not tested by me). This is my suggested solution and it provides the correct output:
'/--------------------------------------\'
'| Sets up the dictionary for equipment |'
'\--------------------------------------/'
inputRowMin = 1
inputRowMax = 173
inputColMin = 1
inputColMax = 2
equipmentCol = 1
dimensionCol = 2
Set equipmentDictionary = CreateObject("Scripting.Dictionary")
Set inputSheet = Application.Sheets(inputSheetName)
Set inputRange = Range(Cells(inputRowMin, inputColMin), Cells(inputRowMax, inputColMax))
Set equipmentCollection = New Collection
For i = 1 To inputRange.Height
thisEquipment = inputRange(i, equipmentCol).Text
nextEquipment = inputRange(i + 1, equipmentCol).Text
thisDimension = inputRange(i, dimensionCol).Text
'The Strings are equal - add thisEquipment to collection and continue
If (StrComp(thisEquipment, nextEquipment, vbTextCompare) = 0) Then
equipmentCollection.Add thisDimension
'The Strings are not equal - add thisEquipment to collection and the collection to the dictionary
Else
equipmentCollection.Add thisDimension
equipmentDictionary.Add thisEquipment, equipmentCollection
Set equipmentCollection = New Collection
End If
Next
'Check input
Dim tmpCollection As Collection
For Each key In equipmentDictionary.Keys
Debug.Print "--------------" & key & "---------------"
Set tmpCollection = equipmentDictionary.Item(key)
For i = 1 To tmpCollection.Count
Debug.Print tmpCollection.Item(i)
Next
Next
Note that this solution assumes that all the equipment are sorted!
Arrays in VBA are more or less like everywhere else with various peculiarities:
Redimensioning an array is possible (although not required).
Most of the array properties (e.g., Sheets array in a Workbook) are 1-based. Although, as rightly pointed out by #TimWilliams, the user-defined arrays are actually 0-based. The array below defines a string array with a length of 11 (10 indicates the upper position).
Other than that and the peculiarities regarding notations, you shouldn't find any problem to deal with VBA arrays.
Dim stringArray(10) As String
stringArray(1) = "first val"
stringArray(2) = "second val"
'etc.
Regarding what you are requesting, you can create a dictionary in VBA and include a list on it (or the VBA equivalent: Collection), here you have a sample code:
Set dict = CreateObject("Scripting.Dictionary")
Set coll = New Collection
coll.Add ("coll1")
coll.Add ("coll2")
coll.Add ("coll3")
If Not dict.Exists("dict1") Then
dict.Add "dict1", coll
End If
Dim curVal As String: curVal = dict("dict1")(3) '-> "coll3"
Set dict = Nothing
You can have dictionaries within dictionaries. No need to use arrays or collections unless you have a specific need to.
Sub FillNestedDictionairies()
Dim dcParent As Scripting.Dictionary
Dim dcChild As Scripting.Dictionary
Dim rCell As Range
Dim vaSplit As Variant
Dim vParentKey As Variant, vChildKey As Variant
Set dcParent = New Scripting.Dictionary
'Don't use currentregion if you have adjacent data
For Each rCell In Sheet2.Range("A1").CurrentRegion.Cells
'assume the text is separated by a space
vaSplit = Split(rCell.Value, Space(1))
'If it's already there, set the child to what's there
If dcParent.Exists(vaSplit(0)) Then
Set dcChild = dcParent.Item(vaSplit(0))
Else 'create a new child
Set dcChild = New Scripting.Dictionary
dcParent.Add vaSplit(0), dcChild
End If
'Assumes unique post-space data - text for Exists if that's not the case
dcChild.Add CStr(vaSplit(1)), vaSplit(1)
Next rCell
'Output to prove it works
For Each vParentKey In dcParent.Keys
For Each vChildKey In dcParent.Item(vParentKey).Keys
Debug.Print vParentKey, vChildKey
Next vChildKey
Next vParentKey
End Sub
I am not that familiar with C++ and Python (been a long time) so I can't really speak to the differences with VBA, but I can say that working with Arrays in VBA is not especially complicated.
In my own humble opinion, the best way to work with dynamic arrays in VBA is to Dimension it to a large number, and shrink it when you are done adding elements to it. Indeed, Redim Preserve, where you redimension the array while saving the values, has a HUGE performance cost. You should NEVER use Redim Preserve inside a loop, the execution would be painfully slow
Adapt the following piece of code, given as an example:
Sub CreateArrays()
Dim wS As Worksheet
Set wS = ActiveSheet
Dim Flanged_connections()
ReDim Flanged_connections(WorksheetFunction.CountIf(wS.Columns(1), _
"Flanged_connections"))
For i = 1 To wS.Cells(1, 1).CurrentRegion.Rows.Count Step 1
If UCase(wS.Cells(i, 1).Value) = "FLANGED_CONNECTIONS" Then ' UCASE = Capitalize everything
Flanged_connections(c1) = wS.Cells(i, 2).Value
End If
Next i
End Sub

pulling out data from a colums in Excel

I have the following Data in Excel.
CHM0123456 SRM0123:01
CHM0123456 SRM0123:02
CHM0123456 SRM0256:12
CHM0123456 SRM0123:03
CHM0123457 SRM0789:01
CHM0123457 SRM0789:02
CHM0123457 SRM0789:03
CHM0123457 SRM0789:04
What I need to do is pull out all the relevent SRM numbers that relate to a single CHM ref. now I have a formular that will do some thing like this
=INDEX($C$2:$C$6, SMALL(IF($B$8=$B$2:$B$6, ROW($B$2:$B$6)-MIN(ROW($B$2:$B$6))+1, ""), ROW(A1)))
however this is a bit untidy and I really want to produce this same using short vb script, do i jsut have to right a loop that will run though and check each row in turn.
For x = 1 to 6555
if Ax = Chm123456
string = string + Bx
else
next
which should give me a final string of
SRM0123:01,SRM123:02,SRM0256:12,SRM0123:03
to use with how i want.
Or is ther a neater way to do this ?
Cheers
Aaron
my current code
For x = 2 To 6555
If Cells(x, 1).Value = "CHM0123456" Then
outstring = outstring + vbCr + Cells(x, 2).Value
End If
Next
MsgBox (outstring)
End Function
I'm not sure what your definition of 'neat' is, but here is a VBA function that I consider very neat and also flexible and it's lightning fast (10k+ entires with no lag). You pass it the CHM you want to look for, then the range to look in. You can pass a third optional paramater to set how each entry is seperated. So in your case you could write (assuming your list is :
=ListUnique(B2, B2:B6555)
You can also use Char(10) as the third parameter to seperat by line breaks, etc.
Function ListUnique(ByVal search_text As String, _
ByVal cell_range As range, _
Optional seperator As String = ", ") As String
Application.ScreenUpdating = False
Dim result As String
Dim i as Long
Dim cell As range
Dim keys As Variant
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
On Error Resume Next
For Each cell In cell_range
If cell.Value = search_text Then
dict.Add cell.Offset(, 1).Value, 1
End If
Next
keys = dict.keys
For i = 0 To UBound(keys)
result = result & (seperator & keys(i))
Next
If Len(result) <> 0 Then
result = Right$(result, (Len(result) - Len(seperator)))
End If
ListUnique = result
Application.ScreenUpdating = True
End Function
How it works: It simple loops through your range looking for the search_string you give it. If it finds it, it adds it to a dictionary object (which will eliminate all dupes). You dump the results in an array then create a string out of them. Technically you can just pass it "B:B" as the search array if you aren't sure where the end of the column is and this function will still work just fine (1/5th of a second for scanning every cell in column B with 1000 unique hits returned).
Another solution would be to do an advancedfilter for Chm123456 and then you could copy those to another range. If you get them in a string array you can use the built-in excel function Join(saString, ",") (only works with string arrays).
Not actual code for you but it points you in a possible direction that can be helpful.
OK, this might be pretty fast for a ton of data. Grabbing the data for each cell takes a ton of time, it is better to grab it all at once. The the unique to paste and then grab the data using
vData=rUnique
where vData is a variant and rUnique is the is the copied cells. This might actually be faster than grabbing each data point point by point (excel internally can copy and paste extremely fast). Another option would be to grab the unique data without having the copy and past happen, here's how:
dim i as long
dim runique as range, reach as range
dim sData as string
dim vdata as variant
set runique=advancedfilter(...) 'Filter in place
set runique=runique.specialcells(xlCellTypeVisible)
for each reach in runique.areas
vdata=reach
for i=lbound(vdata) to ubound(vdata)
sdata=sdata & vdata(i,1)
next l
next reach
Personally, I would prefer the internal copy paste then you could go through each sheet and then grab the data at the very end (this would be pretty fast, faster than looping through each cell). So going through each sheet.
dim wks as worksheet
for each wks in Activeworkbook.Worksheets
if wks.name <> "CopiedToWorksheet" then
advancedfilter(...) 'Copy to bottom of list, so you'll need code for that
end if
next wks
vdata=activeworkbook.sheets("CopiedToWorksheet").usedrange
sData=vdata(1,1)
for i=lbound(vdata) + 1 to ubound(vdata)
sData=sData & ","
next i
The above code should be blazing fast. I don't think you can use Join on a variant, but you could always attempt it, that would make it even faster. You could also try application.worksheetfunctions.contat (or whatever the contatenate function is) to combine the results and then just grab the final result.
On Error Resume Next
wks.ShowAllData
On Error GoTo 0
wks.UsedRange.Rows.Hidden = False
wks.UsedRange.Columns.Hidden = False
rFilterLocation.ClearContents