Excel VBA that searches by header name not column - vba

I need a VBA macro that does the below:
This part works fine, I want it to make a new column on sheet1 and name it header name then color it.
Columns("P:P").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("P1").Select
ActiveCell.FormulaR1C1 = "Header Name"
Range("P1").Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 15773696
.TintAndShade = 0
.PatternTintAndShade = 0
End With
This part however I would like to look for the header name on sheet2 not just the column C (since sometimes the column locations can change)
Range("P2").Select
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[8],Sheet2!C[-15]:C[-14],2,FALSE)"
Range("P2").Select
Selection.AutoFill Destination:=Range("P2:P" & Cells(Rows.Count, "X").End(xlUp).Row)
so basically this is what I want it to do:
on sheet 1 make a new column in P and name it "header name" then I want it to do a vlook up for column x (header 2) on sheet 1 (by name if able) and compare it to sheet2 column a (header 02) and give me the matching information in column B (header 3)
I have used this vlookup =VLOOKUP(X2,Sheet2!A:B,2,FALSE) but I want them to be header names not x,a,b and to search the entire sheet to find the header names.
Column X name: Header 2
Column A name: Header 02
Column B name: Header 3
Column P name: Header Name

Hmm, somehow feels hard to give this away, this is my precious baby for doing the job.
But all I can do is thank stack overflow and all of the community for all they have done, so here goes:
NOTE! I use Dictionaries. To make Dictionaries work, in VBA editor goto Tools > References. In the pop up scroll down to "Microsoft Scripting Runtime" and check the box and click OK.
Option Base 1
Sub TransferData()
Dim Data() As Variant
Dim dataSheet As String
Dim resultSheet As String
Dim headingIndexes As New Dictionary
dataSheet = "Data"
dataStartCell = "A1"
resultSheet = "Result"
Data() = Sheets(dataSheet).Range(dataStartCell).CurrentRegion.Value
Call GetHeadingIndexes(Data(), headingIndexes)
Call Transfer(Data(), headingIndexes, resultSheet)
End Sub
Sub GetHeadingIndexes(ByRef Data() As Variant, ByRef headingIndexes As Dictionary)
'Creates a dictionary with key-value pairs
'
'Creates a dictionary structure with key-value pairs resembling a table:
' [Column Heading] | [Column Index]
' "Actual/Forecast" | 1
' "Brand" | 2
' "Division/ Line of Business" | 3
'
'Now it is easy and quick to find the column index based on column heading.
Dim i As Integer
For i = 1 To UBound(Data(), 2)
headingIndexes.Add Data(1, i), i 'Make key-value pairs out of column heading and column index
Next i
End Sub
Sub Transfer(ByRef Data() As Variant, ByRef headingIndexes As Dictionary, resultSheet As String)
Application.ScreenUpdating = False
Dim resultColumnHeading As String
Dim resultSheetColumnNumber As Integer
Dim dataColumnNumber As Integer
Dim row As Integer
'Loop through columns in result sheet. Assumes you have 16 columns
For resultSheetColumnNumber = 1 To 16
'Find the correct column in Data()
resultColumnHeading = resultSheet.Cells(1, resultSheetColumnNumber)
dataColumnNumber = headingIndexes(resultColumnHeading)
For row = 2 To UBound(Data(), 1)
'Transfer data from Data() array to the cell in resultSheet
'Note, referencing each cell like this is really slow, it is better to create a resultArray similar to the data array (called Data() in this example). However, explaining all the nuances would take a one hour phone call, and gets far from the question at hand)
resultSheet.Cells(row, resultSheetColumnNumber) = Data(row, dataColumnNumber)
Next row
Next resultSheetColumnNumber
Application.ScreenUpdating = True
End Sub

It might work if you change this:
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[8],Sheet2!C[-15]:C[-14],2,FALSE)"
to:
ActiveCell.Formula = "=vlookup(X" & ActiveCell.row & ",Sheet2!A:B,2,0)"
But that being said, be careful with ActiveCell and .Select. You may want to check out How to Avoid Using Select in VBA Macros
EDIT:
I've amended/added to the code to take into consideration your need for flexibility with regards to where the columns of data are located.
Sub test3()
'use the Header2sheet1column variable to hold the column number that "Header 2" is found in on sheet 1
Dim Header2sheet1column As Long
'search for "Header 2" across row 1 of sheet1 and remember the column number
Header2sheet1column = Application.Match("Header 2", ThisWorkbook.Sheets("Sheet1").Range("$1:$1"), 0)
'use the Header2sheet2column variable to hold the column number that "Header 2" is found in on sheet 2
Dim Header2sheet2column As Long
'search for "Header 2" across row 1 of sheet2 and remember the column number
Header2sheet2column = Application.Match("Header 2", ThisWorkbook.Sheets("Sheet2").Range("$1:$1"), 0)
'use the lookuprange variable to hold the range on sheet2 that will be used in the vlookup formula
Dim lookuprange As Range
'using With just so I don't have to type ThisWorkbook.Sheets("Sheet2") a bajillion times in the next couple lines
With ThisWorkbook.Sheets("Sheet2")
'set lookuprange variable - will start at column that "Header 2" is found on sheet 2 and will go to last row/column of the sheet
'having extra columns at the end of your vlookup formula isn't going to hurt. the
Set lookuprange = .Range(.Cells(1, Header2sheet2column), .Cells(.Rows.Count, .Columns.Count))
'put formula into Cell P2 on sheet1
ThisWorkbook.Sheets("Sheet1").Range("P2").Formula = "=vlookup(" & ThisWorkbook.Sheets("Sheet1").Cells(2, Header2sheet1column).Address(RowAbsolute:=False) & ",Sheet2!" _
& lookuprange.Address & "," _
& Header2sheet2column & ",0)"
End With
'using With again just so I don't have to type ThisWorkbook.Sheets("Sheet1") a bajillion times in the next couple lines
With ThisWorkbook.Sheets("Sheet1")
'fill formula in column P down to the row that the column
.Range("P2").AutoFill Destination:=.Range("P2:P" & .Cells(.Rows.Count, Header2sheet1column).End(xlUp).Row)
End With
End Sub

You'd be better off using named ranges that are created using the headers for each column. Then your vlookup could just refer to the names rather than the cell references.
To get an idea how to do this start recording a macro then choose your columns and Insert - Names - Create. You can adapt the macro to recreate the names every time your spreadsheet changes. The vlookups won't need to be changed because they will point to the named ranges wherever they are.

I'm far from a VBA expert. Two things in VBA plagued me for a long time until recently.
"Number Stored as Text" error
Find column by first row 'Name' not 'Column Letter'
I use this in a macro to copy & reorder columns in a new sheet:
Sub ColumnReorder()
'**********************************************************
'Paste this macro into the Workbook of each new "Employee_List_Weekly_Update"
'Functionality:
'1. Column order in the "Employee_List_Weekly_Update" worksheet changes fairly often.
' The macro will find each column by header name,
' select that column and copy it to the new sheet.
'2. The macro also converts "Employee ID#" to a number,
' removing the "Number saved as Text" error.
'**********************************************************
'Create new sheet
Sheets.Add.Name = "Roster_Columns_Reordered"
'Repeat for each column or range - For each new section change Dim letter
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Employee ID#
Dim a As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
a = Application.WorksheetFunction.Match("Employee ID#", Range("A1:BB1"), 0)
Columns(a).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("A1").Select
ActiveSheet.Paste
'Use TextToColumns to convert "Number Stored as Text "
Selection.TextToColumns _
Destination:=Range("A:A"), _
DataType:=xlDelimited
'Find Column in "Employee_List_Weekly_Update" - Copy it - Paste it in "Roster_Columns_Reordered" - Name
Dim b As Integer
Sheets("Employee_List_Weekly_Update").Select
Set rngData = Range("A1").CurrentRegion
b = Application.WorksheetFunction.Match("Name", Range("A1:BB1"), 0)
Columns(b).Select
Selection.Copy
Sheets("Roster_Columns_Reordered").Select
Range("B1").Select
ActiveSheet.Paste
'Go to "Roster_Columns_Reordered" - Add AutoFilter - Freeze Top Row
Rows("1:1").Select
Selection.AutoFilter
With ActiveWindow
.SplitColumn = 2
.SplitRow = 1
End With
Rows("2:2").Select
ActiveWindow.FreezePanes = True
Range("A1").Select
End Sub

Related

Macro VBA to Copy Column based on Header and Paste into another Sheet

Background: This is my first time dealing with macros. I will have two worksheets that I’ll be using. The first sheet, ‘Source’ will have data available. The second sheet, ‘Final’ will be blank and is going to be where the macro will be pasting the data I’d like it to collect from the ‘Source’ sheet.
* I want the macro to find the specified header in the ‘Source’ sheet, copy that cell containing the header all the way down to the last row of existing data (instead of the entire column), and paste it onto the ‘Final’ sheet in a specified column (A, B, C, etc.). *
The reason why I have to specify which headers to find is because the headers in the ‘Source’ sheet won’t always be in the same position, but the ‘Final’ sheet’s headers will always be in the same position – so I CAN’T just record macros copying column A in ‘Source’ sheet and pasting in column A in ‘Final’ sheet. Also, one day the ‘Source’ sheet may have 170 rows of data, and another day it may have 180 rows.
Although, it would probably be best to copy the entire column since one of the columns will have a few empty cells rather than to the last row of existing data. I’m assuming it would stop copying when it reaches the first empty cell in the column chosen which would leave out the remaining data after that empty cell in the column – correct me if I’m wrong. If copying the entire column is the best way, then, please provide that as part of the possible solution. I’ve attached an example of the before & after result I would like accomplished:
Example of Result
Find Header=X, copy entire column -> Paste into A1 in ‘Final’ sheet
Find Header=Y, copy entire column -> Paste into B1 in ‘Final’ sheet
Etc..
I’m sorry if my wording isn’t accurate – I tried to explain the best I could. It’d be awesome if someone could help me out on this! Thanks!
u can try with this. i think its clear and step-by-step. it can be very optimized, but to start with vba i think its better this way.
the name of the column must be the same in both sheets.
Sub teste()
Dim val
searchText = "TEXT TO SEARCH"
Sheets("sheet1").Select ' origin sheet
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
x = Selection.Columns.Count ' get number of columns
For i = 1 To x 'iterate trough origin columns
val = Cells(1, i).Value
If val = searchText Then
Cells(1, i).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("sheet2").Select ' destination sheet
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
y = Selection.Columns.Count ' get number of columns
For j = 1 To y 'iterate trough destination columns
If Cells(1, j).Value = searchText Then
Cells(1, j).Select
ActiveSheet.Paste
Exit Sub
End If
Next j
End If
Next i
End Sub
good luck
I modified an answer I gave to another user with similar problem for your case,
I use dictionary function in most of my data sheets so that I can shift columns around without breaking the code, the below code you can shift your columns around and it will still work
the only main restriction is
1. your header names must be unique
2. your header name of interest must be exactly the same.
i.e. your source header of interest is PETER then your Data table should have a header with PETER and it must be unique.
Sub RetrieveData()
Dim wb As Workbook
Dim ws_A As Worksheet
Dim ws_B As Worksheet
Dim HeaderRow_A As Long
Dim HeaderLastColumn_A As Long
Dim TableColStart_A As Long
Dim NameList_A As Object
Dim SourceDataStart As Long
Dim SourceLastRow As Long
Dim Source As Variant
Dim i As Long
Dim ws_B_lastCol As Long
Dim NextEntryline As Long
Dim SourceCol_A As Long
Set wb = ActiveWorkbook
Set ws_A = wb.Worksheets("Sheet A")
Set ws_B = wb.Worksheets("Sheet B")
Set NameList_A = CreateObject("Scripting.Dictionary")
With ws_A
SourceDataStart = 2
HeaderRow_A = 1 'set the header row in sheet A
TableColStart_A = 1 'Set start col in sheet A
HeaderLastColumn_A = .Cells(HeaderRow_A, Columns.Count).End(xlToLeft).Column 'Get number of NAMEs you have
For i = TableColStart_A To HeaderLastColumn_A
If Not NameList_A.Exists(UCase(.Cells(HeaderRow_A, i).Value)) Then 'check if the name exists in the dictionary
NameList_A.Add UCase(.Cells(HeaderRow_A, i).Value), i 'if does not exist record name as KEY and Column number as value in dictionary
End If
Next i
End With
With ws_B 'worksheet you want to paste data into
ws_B_lastCol = .Cells(HeaderRow_A, Columns.Count).End(xlToLeft).Column ' Get number of DATA you have in sheet B
For i = 1 To ws_B_lastCol 'for each data
SourceCol_A = NameList_A(UCase(.Cells(1, i).Value)) 'get the column where the name is in Sheet A from the dictionaary
If SourceCol_A <> 0 Then 'if 0 means the name doesnt exists
SourceLastRow = ws_A.Cells(Rows.Count, SourceCol_A).End(xlUp).Row
Set Source = ws_A.Range(ws_A.Cells(SourceDataStart, SourceCol_A), ws_A.Cells(SourceLastRow, SourceCol_A))
NextEntryline = .Cells(Rows.Count, i).End(xlUp).Row + 1 'get the next entry line of the particular name in sheet A
.Range(.Cells(NextEntryline, i), _
.Cells(NextEntryline, i)) _
.Resize(Source.Rows.Count, Source.Columns.Count).Cells.Value = Source.Cells.Value
End If
Next i
End With
End Sub

Paste values in dynamic range excel vba

I am writing a script where I want to enable a search in a Database, presenting the results of the search queries in a different worksheet (which I have named Results), so that users do not have access to the whole database at the same time.
In order to do this I want to copy values from the "Database" worksheet into the "Results" worksheet. I have succeeded in selecting the right data from the "Database", in respect to any specific search criteria. I did this with the following code:
With Sheets("Database")
.Range(.Cells(i, 1), .Cells(i, 9)).Copy
End With
Now I want to paste the results into the "Results" spreadsheet and I have done so by writing the following:
Sheets("Results").Range("B600").End(xlUp).Offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
By doing this, I don't quite understand:
if I have strictly defined the paste range as between the first empty row and B600 or;
if I am just defining the beginning of the paste range and, in the case that the search results exceed the 600th row, they will still be pasted after this row.
I ask this because, as the database grows, I will certainly need to guarantee a paste range greater than B600.
I have researched on it but cannot seem to be absolutely sure of what I have done exactly. I must say that I know that the first empty row in the "Results" database will always be 12. In this case, I know that I basically want to paste the search results from the 12th row on. Maybe there is a more straight-forward way to do this.
This is the entire code, for reference:
Private Sub SearchButton_Click()
'This is the search function
'1. declare variables
'2. clear old search results
'3. Find records that match criteria and paste them
Dim country As String
Dim Category As String
Dim Subcategory As String
Dim finalrow As Integer
Dim i As Integer 'row counter
'Erase any entries from the Results sheet
Sheets("Results").Range("B10:J200000").ClearContents
'Deformat any tables in the Results sheet
For Each tbl In Sheets("Results").ListObjects
tbl.Clear
Next
'Define the user-inputed variables
country = Sheets("Results").Range("D5").Value
Category = Sheets("Results").Range("D6").Value
Subcategory = Sheets("Results").Range("D7").Value
finalrow = Sheets("Database").Range("A" & Rows.Count).End(xlUp).Row
'If statement for search
'For every variable i, start comparing from row 2 until the final row
For i = 2 To finalrow
'If the country field is left empty
If country = "" Then
Sheets("Results").Range("B10:J200000").Clear
MsgBox "You must select a country in order to search the database. Please do so in the drop-down list provided."
Sheets("Results").Range("D5").ClearContents
Sheets("Results").Range("D6").ClearContents
Sheets("Results").Range("D7").ClearContents
Exit Sub
'If the country field is filled in and there results from the search made
ElseIf Sheets("Database").Cells(i, 1) = country And _
(Sheets("Database").Cells(i, 3) = Category Or Category = "") And _
(Sheets("Database").Cells(i, 4) = Subcategory Or Subcategory = "") Then
'Copy the headers of the table
With Sheets("Database")
.Range("A1:I1").Copy
End With
Sheets("Results").Range("B10:J10").PasteSpecial
'Copy the rows of the table that match the search query
With Sheets("Database")
.Range(.Cells(i, 1), .Cells(i, 9)).Copy
End With
Sheets("Results").Range("B600").End(xlUp).Offset(1, 0).PasteSpecial xlPasteFormulasAndNumberFormats
'Hides search form
Me.Hide
End If
Next i
'Toggle Results sheet
Sheets("Results").Activate
'Format results as a table
Set rng = Range(Range("B10"), Range("B10").End(xlUp).SpecialCells(xlLastCell))
Set table = Sheets("Results").ListObjects.Add(xlSrcRange, rng, , xlYes)
table.TableStyle = "TableStyleMedium13"
Range("B11").Select
'Make Excel window visible
Application.Visible = True
End Sub
Thank you very much for your help.
You can count from the bottom of the sheet upto the last used cell in column B, and then OFFSET by 1 row. This prevents you needing to worry about
a) that the range to paste to starts from row 12 (they should contain values), and
b) that you are currently using a hard-coded 'anchor' of B600 which will need updating as the data grows.
Sample code:
Dim ws As Worksheet
Dim rngColumnBUsed As Range
Dim lngFirstEmptyRow As Long
Set ws = ThisWorkbook.Sheets("Results")
Set rngColumnBUsed = ws.Range("B" & ws.Rows.Count).End(xlUp).Offset(1, 0)
lngFirstEmptyRow = rngColumnBUsed.Row
Two ListObjects tblDatabase and tblResults
tblResults data gets cleared
A filter is applied to the second, third and fourth columns of tblDatabase
If there are less than 588 results, we copy the filtered records from tblDatabase to tblResults
If there are more than 588 results then we resize the filtered records' range down to the first 588 records and then copy them to tblResults
We never worry about formatting because tblResults keeps it's original format.
Sub ListObjectDemo()
Dim tblDatabase As ListObject, tblResults As ListObject
Set tblDatabase = Worksheets("Database").ListObjects("tblDatabase")
Set tblResults = Worksheets("Results").ListObjects("tblResults")
If Not tblResults.DataBodyRange Is Nothing Then tblResults.DataBodyRange.ClearContents
With tblDatabase.Range
.AutoFilter Field:=2, Criteria1:="Test A"
.AutoFilter Field:=3, Criteria1:="East"
.AutoFilter Field:=4, Criteria1:="Algeria"
End With
With tblDatabase.DataBodyRange
If .Rows.Count <= 588 Then
.Copy tblResults.ListRows.Add.Range
Else
.Resize(588).Copy tblResults.ListRows.Add.Range
End If
End With
End Sub
Dim searchdata as range, inputfromuser as string
inputfromuser = inputbox("type what you wanna search")
set searchdata = sheets("Database").find(inputfromuser).select
searchdata = activecell.value or activecell.offset(10,5).value
sheets("results").activate
with sheets("result")
range("a12",range("a12").end(xldown)).offset(1,0).select
searchdata.copy destination:= activecell
activecell.offset(1,0).select
end with
Not sure, if I understood you corectly mate.
I dont haveexcel sheet or VBE editor. Just wrote this directly on website. Pls amend as per your need.

Counting unique text values and making dynamic dashboard in VBA

I have a reference file for a dashboard I am making. I want to be able to count the unique org names in a reference file column and make that number the amount of column headers in my separate dashboard. I used macro recorder to make it unique, but I am unsure of how to translate this into making a dynamic number of columns for my dashboard based on the count of unique org names in the ref file. Here's a picture attachment example of what the reference file could look like. So, if it counts that there are 5 unique names, I would like the separate dashboard to make 5 columns with those names as headers in each column.
Sub Macro1()
' Macro1 Macro
Columns("F:F").Select
Range("F1:F10000000000").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Columns _
("O:O"), Unique:=True
ActiveCell.FormulaR1C1 = "=ROWS(R[-11]C:R[-2]C)"
End Sub
Here's a way to do it by loading the unique results into an array. This assumes the column headers go into A1.
Sub Macro1()
Dim wsRef As Worksheet
Dim wsDB As Worksheet
Set wsRef = Worksheets("reference")
Set wsDB = Worksheets("Dashboard")
With wsRef
.Range("C1:C9").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=.Range("F1"), Unique:=True
Dim arrValues As Variant
arrValues = .Range("F2", .Range("F" & .Rows.Count).End(xlUp))
End With
With wsDB
.Range(.Range("A1"), .Cells(1, UBound(arrValues))).Value = Application.WorksheetFunction.Transpose(arrValues)
End With
End Sub
Use a Dictionary object to get your unique values, then loop through the dictionary items to make your column headers. eg,
myCol = 1
For Each item in oDic.items
'Presuming you want your headers to start at A1
Cells(1, myCol).Value = item
myCol = myCol + 1
Next

VBA - Search Column by Specific Name without Text box

I am trying to copy specific columns by the column name and copy it to a new worksheet. I found this code online but I would like to have the cloumn names in the vba code instead of having a textbox pop up and me writing each one in at a time.
Sub copycolumns()
Dim strColRng As String
Dim strSheetName As String
Dim intNoofCols As Integer
Dim strColName() As String
Dim strCurSheetName As String
'To get the No. of Columns Available to Search
intRng = 65
'To get the No. of Columns to copy and paste
intNoofCols = 10
'To set size of the Array
ReDim Preserve strColName(intNoofCols)
For i = 0 To intNoofCols - 1
'To Get the Column Name to Search
strColName(i) = Array(Array("POS", "POS"), Array("Product Code", "Product Code"), Array("Product Name", "Product Name"), Array("Currency", "Currency"), Array("Nominal Source", "Nominal Source"), Array("Maturity Date", "Maturity Date"), Array("Nominal USD", "Nominal USD"), Array("BV Source", "BV Source"), Array("ISIN", "ISIN"), Array("Daily NII USD", "Daily NII USD"))
' InputBox("Enter the Column Name to Copy?", "Column Name")
Next
'To get the Sheet Name to paste the content
strSheetName = InputBox("Enter the Sheet Name to Paste?", "Sheet Name")
'To store the Current Sheet Name where to copy
strCurSheetName = ActiveSheet.Name
For j = 0 To intNoofCols - 1 'To get the Column Names from the Array
For i = 1 To intRng
'To Select the Sheet which column to copy
Sheets(strCurSheetName).Select
'Store the Cell Value
strVal = Cells(1, i)
'Check the Value with the User given column name
If UCase(strVal) = UCase(Trim(strColName(j))) Then
'Select and Copy
Cells(1, i).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
'Select and Paste
Sheets(strSheetName).Select
Cells(1, j + 1).Select
Range(Selection, Selection.End(xlDown)).PasteSpecial xlPasteValues
' ActiveSheet.Paste
End If
Next
Next
I appreciate any help. Thanks!
So, if I understand correctly, you want the strColName variable to hold the array you defined, instead of the program looping and asking the user to fill the array? In that case, use:
Dim strColName() As String
strColName = Split("POS,Product Code,Product Name,Currency,Nominal Source,Maturity Date,Nominal USD,BV Source,ISIN,Daily NII USD", ",")
Problem is, you defined the strColName as an array of Strings, and you input arrays. Also, you defined the array inside the loop, so it will execute 10 times. You can delete the redim statement, because you define the number of members of the array when you make the array.
I use this all the time
'1 = DELETE all columns IN list
'2 = DELETE all columns NOT in list
'3 = MOVE all columns IN List to NEW Sheet
'4 = MOVE all columns NOT in List to NEW Sheet
'sSource = Source Sheet for Deleting or Moving To
'tTarget = Target Sheet for Moving Columns To
'n = offset the numer of columns when moving columns n = 0 for no offset
Sub MoveOrDelete()
fDeleteOrMove 3, "MySheetNameSoure", "MySheetNameTarget", 0, Array("ColName1", "ColName2", "ColName3")
End Sub
'THIS IS THE FUNCTION FOR MOVE/DELETE
Sub fDeleteOrMove(cWhat As Integer, sSource As String, tTarget As String, n As Integer, myList As Variant)
Dim wsS As Excel.Worksheet
Dim wsT As Excel.Worksheet
Dim LC As Long
Dim mycol As Long
Dim x
ActiveWorkbook.Worksheets(sSource).Select
Set wsS = ActiveWorkbook.Worksheets(sSource) 'Source Sheet for Deleting or Moving To
Set wsT = ActiveWorkbook.Worksheets(tTarget) 'Target Sheet for Moving Columns To
'Get Last Row of "Source" Sheet
LC = wsS.Cells(1, Columns.Count).End(xlToLeft).Column
For mycol = LC To 1 Step -1
x = ""
On Error Resume Next
x = WorksheetFunction.match(Cells(1, mycol), myList, 0)
Select Case cWhat
Case 1
'Delete all columns IN list
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Delete
Case 2
'Delete all columns NOT in list
If Not IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Delete
Case 3
'Move all columns IN List to NEW Sheet
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(x).Offset(, n)
Case 4
'Move all columns NOT in List to NEW SheeT
If Not IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(mycol).Offset(, n)
'Delete the EMPTY columns that were not moved from "Target" sheet
If IsNumeric(x) Then wsS.Columns(mycol).EntireColumn.Copy _
Destination:=wsT.Columns(mycol).Offset(, n).Delete
End Select
Next mycol
ActiveWorkbook.Worksheets(tTarget).Select
End Sub

Looping through all available autofilter criteria one at a time in vba

I was wondering if there was a way to get all the different autofilter criteria in a list in order to iterate through each criteria, to in the end copy and paste each different table that would appear to a separate sheet as it iterates through.
Ideally this would be run n times:
ActiveSheet.Range(AllRows).AutoFilter Field:=10, Criteria1:=CritVariable
Where n is the number of different CritVariables there are.
I'd like to stress that I know how to copy and paste in the macro itself, but I was curious how to iterate through all the different criteria because the criteria could be different depending on the day. If a list of it isn't available how would I best go about iterating through the criteria?
You can study and adapt the following. Here is an outline of what is going on.
I have a staff-table starting at cell A5, with a list of Offices in
column G;
I'm copying from G5 downwards (assuming there are no blanks in this column's data) to W1;
From range W1 downwards I am removing duplicates;
Then I'm looping through this data, using Advanced Filter to copy the data for each office to an area starting at cell Z1;
This filtered data is then moved (Cut) to a new worksheet, which is named from the current Office name (the criteria);
After each Advanced Filter the cell W2 is deleted, making the value in W3 move up, so that it can be used for the next filter operation.
This does mean that when you press Ctrl-End to go to the last-used cell it goes further than it needs to. You can find a way to resolve this if necessary ;).
Sub SheetsFromFilter()
Dim wsCurrent As Worksheet
Dim wsNew As Worksheet
Dim iLeft As Integer
Set wsCurrent = ActiveSheet
Application.ScreenUpdating = False
Range("G5", Range("G5").End(xlDown)).Copy Range("W1")
Range("W1").CurrentRegion.RemoveDuplicates Columns:=1, Header:=xlYes
iLeft = Range("W1").CurrentRegion.Rows.Count - 1
Do While iLeft > 0
wsCurrent.Range("A5").CurrentRegion.AdvancedFilter xlFilterCopy, _
wsCurrent.Range("W1:W2"), wsCurrent.Range("Z1")
Set wsNew = Worksheets.Add
wsCurrent.Range("Z1").CurrentRegion.Cut wsNew.Range("A1")
wsNew.Name = wsCurrent.Range("W2").Value
wsCurrent.Range("W2").Delete xlShiftUp
iLeft = iLeft - 1
Loop
wsCurrent.Range("W1").Clear
Application.ScreenUpdating = True
End Sub
BTW I don't intend to modify this for your specific file; this is something that you should do (or pay someone to do ;) ).
BTW It could be done using the normal (rather than Advanced) Filter. You would still copy the column and remove duplicates. This would have the benefit of not increasing the apparent size of the worksheet too much. But I decided to do it this way ;).
Added: Well, I felt inspired to achieve this with AutoFilter as well:
Sub SheetsFromAutoFilter()
Dim wsCurrent As Worksheet
Dim wsNew As Worksheet
Dim iLeft As Integer
Set wsCurrent = ActiveSheet
Application.ScreenUpdating = False
Range("G5", Range("G5").End(xlDown)).Copy Range("W1")
Range("W1").CurrentRegion.RemoveDuplicates Columns:=1, Header:=xlYes
iLeft = Range("W1").CurrentRegion.Rows.Count - 1
Do While iLeft > 0
Set wsNew = Worksheets.Add
With wsCurrent.Range("A5").CurrentRegion
.AutoFilter field:=7, _
Criteria1:=wsCurrent.Range("W1").Offset(iLeft).Value
.Copy wsNew.Range("A1")
.AutoFilter
End With
wsNew.Name = wsCurrent.Range("W1").Offset(iLeft).Value
iLeft = iLeft - 1
Loop
wsCurrent.Range("W1").CurrentRegion.Clear
Application.ScreenUpdating = True
End Sub
[Both procedures could be improved using Defined Names and some error handling/checking.]
if you want you can build a new collection which will have an array of only unique values and then loop over them. you will know that each
I know it's late and you've already selected an answer, but I'm working on a similar project involving a pivot table and decided to do it this way:
'Here I'm Filtering a column of Week numbers to get rid of non-numbers
'From a pivot table
'I select sheet where my underlying pivot data is located and establish the range
'My data is in column 2 and it ends after "DSLastRow" Rows starting at Row 2
Sheets("DataSheet").Select
DSLastRow = Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'I create and redim an array that is large enough to hold all of the data in the range
Dim FilterCriteria(): RedimFilterCriteria(1 To DSLastRow)
For r = 2 To DSLastRow 'r for row / my data has a header in row 1
If Cells(r, 2).Value <> "" Then 'again, starting in column B (2)
'Check if it's already in the FilterCriteria Array
For CheckFCA = 1 To r
'Jumps to next row if it finds a match
If FilterCriteria(CheckFCA) = Cells(r, 2).Value Then GoTo Nextr
'Saves the value and jumps to next row if it reaches an empty value in the array
If IsEmpty(FilterCriteria(CheckFCA)) Then
FilterCriteria(CheckFCA) = Cells(r, 2)
GoTo Nextr
End If
Next CheckFCA
End if
Nextr:
Next r
'At this point FilterCriteria() is filled with all of the unique values
'I'm filtering a pivot table which is why I created the unique array from
'the source data, but you should be able to just loop through the table
Sheets("Pivot").Select
ActiveSheet.PivotTables("ReportPivot").PivotFields("Week").ClearAllFilters
With ActiveSheet.PivotTables("ReportPivot").PivotFields("Week")
For FilterPivot = 1 To DSLastRow
'I'm filtering out all non-numeric items
If IsEmpty(FilterCriteria(FilterPivot)) Then Exit For
If Not IsNumeric(FilterCriteria(FilterPivot)) Then
.PivotItems(FilterCriteria(FilterPivot)).Visible = False
End If
Next FilterPivot
End With