VBA, Advanced Filter Based on Header - vba

I have an advanced filter macro to run in excel that filters certain columns for unique data. I have a bunch of workbooks as well, and have certain headers that are identical across these workbooks, but headers in each workbook may differ in columns.
So header 'Stackoverflow' may be Column F in one file, and Column E in another. I just want to alter my code to something generic so it gets filter this column with a particular header no matter which workbook (Instead of filtering e:e, f:f, etc). any input is appreciated.
EDIT: this is my full macro, the part where I filter is a bit further down.
Here is my code:
Sub stkoverflow()
Dim ws As Worksheet
Dim wks As Excel.Worksheet
Dim wksSummary As Excel.Worksheet
Dim y As Range
Dim intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range
For Each ws In ActiveWorkbook.Worksheets
ws.Activate
lr = Cells(Rows.Count, "c").End(3).Row
Set myrg = Range("f2:f" & lr)
myrg.ClearContents
myrg.Formula = "=IFERROR(LEFT(e2,FIND(""_"",e2,1)-1),LEFT(e2,2))"
myrg.Value = myrg.Value
Range("f1").Value = "Test"
Next ws
On Error Resume Next
Set wksSummary = Excel.ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = Excel.ActiveWorkbook.Worksheets.Add
wksSummary.Name = "Unique data"
End If
For Each wks In Excel.ActiveWorkbook.Worksheets
With wksSummary
If wks.Name <> .Name Then
' THIS SECTION OF CODE IS POINTLESS. 'r' WILL ALWAYS BE DECLARED IRRESPECTIVE OF THE 'IF' STATEMENT
' If Application.WorksheetFunction.CountA(wks.Range("f:f")) Then
' Dim r As Range
' End If
With wksSummary
If wks.Name <> .Name Then
If Application.WorksheetFunction.CountA(wks.Range("a:a")) Then
Set r = .Cells(.Cells(.Rows.Count, 4).End(xlUp).Row + 1, 4)
Set y = .Cells(.Cells(.Rows.Count, 5).End(xlUp).Row + 0, 5)
If WorksheetFunction.CountA(wks.Range("f:f")) > 1 Then
If WorksheetFunction.CountA(wks.Range("a:a")) > 1 Then
wks.Range("f:f").AdvancedFilter xlFilterCopy, , r, True
wks.Range("a:a").AdvancedFilter xlFilterCopy, , y, True
Else
r = "N/A"
y = "N/A"
End If
End If
r.Delete xlShiftUp
End If
' I HAVE INSERTED BLOCK ENDINGS FROM HERE, AND CHANGED THE INDENTING OF THE SUBSEQUENT CODE TO FIT
' The next 4 lines are all inserted
End If
End With
End If
End With
' I have removed 4 x 'tab' indents from all of the code below
Next wks
Range("A1").Value = "File Name "
Range("B1").Value = "Sheet Name "
Range("D1").Value = "Scenario Name"
intRow = 2
For i = 1 To Sheets.Count
If Sheets(i).Name <> ActiveSheet.Name Then
Cells(intRow, 2) = Sheets(i).Name
Cells(intRow, 1) = ActiveWorkbook.Name
intRow = intRow + 1
End If
Next i
End Sub

Here is one way to get the column number of a header
Option Explicit
Public Function hdrCol(ByRef ws As Worksheet, _
ByVal hdrName As String, _
Optional hdrRow As Long = 1, _
Optional matchLtrCase As Boolean = True) As Long
Dim found As Range, foundCol As Long
If Not ws Is Nothing Then
hdrRow = Abs(hdrRow)
hdrName = Trim(hdrName)
If hdrRow > 0 And Len(hdrName) > 0 Then
Set found = ws.UsedRange.Rows.Find(What:=hdrName, _
LookIn:=xlFormulas, _
LookAt:=xlWhole, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
matchCase:=matchLtrCase)
If Not found Is Nothing Then foundCol = found.Column
End If
End If
hdrCol = foundCol
End Function
To test it:
Public Sub testHeader()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
MsgBox hdrCol(ws, "Stackoverflow")
Next
End Sub
.
Edit:
Changes I'd make to your code (not tested)
Option Explicit
Public Sub stkoverflow()
Dim wb As Workbook, ws As Worksheet, wsSummary As Worksheet, lr As Long
Dim y As Range, r As Range, thisRow As Long, colA As Range, colF As Range
Set wb = ActiveWorkbook
For Each ws In wb.Worksheets
lr = ws.Cells(ws.Rows.Count, "C").End(3).Row
With ws.Range("F2:F" & lr)
.ClearContents
.Formula = "=IFERROR(LEFT(E2,FIND(""_"",E2,1)-1),LEFT(E2,2))"
.Value = .Value
End With
ws.Range("F1").Value = "Test"
If ws.Name = "Unique data" Then Set wsSummary = ws
Next ws
If wsSummary Is Nothing Then
Set wsSummary = wb.Worksheets.Add
wsSummary.Name = "Unique data"
End If
For Each ws In wb.Worksheets
With wsSummary
If ws.Name <> .Name Then
'...
'Determine dynamic columns based on header
Set colA = ws.Columns(hdrCol(ws, "YOUR_HEADER_NAME_FOR_COL_A", 1, True))
Set colF = ws.Columns(hdrCol(ws, "YOUR_HEADER_NAME_FOR_COL_F", 1, True))
If ws.Name <> .Name Then
If Application.WorksheetFunction.CountA(colA) Then
Set r = .Cells(.Cells(.Rows.Count, 4).End(xlUp).Row + 1, 4)
Set y = .Cells(.Cells(.Rows.Count, 5).End(xlUp).Row + 0, 5)
If WorksheetFunction.CountA(colF) > 1 Then
If WorksheetFunction.CountA(colA) > 1 Then
colF.AdvancedFilter xlFilterCopy, , r, True
colA.AdvancedFilter xlFilterCopy, , y, True
Else
r = "N/A"
y = "N/A"
End If
End If
r.Delete xlShiftUp
End If
'...
End If
End If
End With
'...
Next ws
With ActiveSheet 'not sure about the ActiveSheet...
.Range("A1").Value = "File Name "
.Range("B1").Value = "Sheet Name "
.Range("D1").Value = "Scenario Name"
End With
thisRow = 2
For Each ws In wb.Worksheets
If ws.Name <> ActiveSheet.Name Then
ActiveSheet.Cells(thisRow, 2) = ws.Name
ActiveSheet.Cells(thisRow, 1) = wb.Name
thisRow = thisRow + 1
End If
Next
End Sub
'---------------------------------------------------------------------------------------

Related

copy from multiple sheets to seperate workbook

I need to write some code to run through each worksheet of a specific workbook, and copy specific cells to a separate workbook. I'm having trouble specifying the destination worksheet to copy to. What I have so far:
Private Sub CommandButton1_Click()
Dim wb As Workbook, wbhold As Workbook
Dim ws As Worksheet, wshold As Worksheet
Dim holdCount As Integer
Dim cellColour As Long
Dim cell As Range, rng As Range
Set wb = Workbooks.Open("blahblah.xls")
Set wbhold = Workbooks.Open("blahblah2.xlsm")
holdCount = 0
cellColour = RGB(255, 153, 0)
rownumber = 0
For Each ws In wb.Worksheets
With ws
Set rng = ws.Range("A1:A20")
For Each cell In rng
rownumber = rownumber + 1
If cell.Interior.Color = cellColour Then
Range("A" & rownumber & ":B" & rownumber).Select
Selection.Copy
wbhold.Activate
Sheets("Hold Data").Activate
Cells.Offset(1, 0).PasteSpecial
Application.CutCopyMode = False
With Selection.Font
.Name = "Arial"
.Size = 10
wb.Activate
End With
holdCount = holdCount + 1
End If
Next cell
End With
Next ws
Application.DisplayAlerts = False
wb.Close
MsgBox "found " & holdCount
End Sub
But the line: Sheets("Hold Data").Activate keeps throwing up a "Subscript out of range" error. I've been playing around with the code for about 2 hours now, trying to get it to work, but to no avail. Any ideas?
This should do what you want a little faster:
Private Sub CommandButton1_Click()
Dim wb As Workbook, wbhold As Workbook
Dim ws As Worksheet, wshold As Worksheet
Dim holdCount As Integer
Dim cellColour As Long
Dim cell As Range, rng As Range
Dim outrow As Long
Application.ScreenUpdating = False
Set wb = Workbooks.Open("blahblah.xls")
Set wbhold = Workbooks.Open("blahblah2.xlsm")
Set wshold = wbhold.Worksheets("Hold Data")
holdCount = 0
cellColour = RGB(255, 153, 0)
outrow = 1
For Each ws In wb.Worksheets
Set rng = Nothing
With ws
For Each cell In .Range("A1:A20")
If cell.Interior.Color = cellColour Then
If rng Is Nothing Then
Set rng = cell.resize(, 2)
Else
Set rng = Union(rng, cell.Resize(, 2))
End If
holdCount = holdCount + 1
End If
If Not rng Is Nothing Then
rng.Copy wshold.Cells(outrow, "A")
outrow = outrow + rng.Cells.Count \ 2
End If
Next cell
End With
Next ws
With wshold.Cells(1, "A").CurrentRegion.Font
.Name = "Arial"
.Size = 10
End With
wb.Close False
Application.ScreenUpdating = True
MsgBox "found " & holdCount
End Sub

VBA Advanced AutoFilter + Create new sheets based on range

I need to create new tabs in a workbook based upon a range of cells in a worksheet template. I also want to delete rows of data that do not match the tab name. For example, from the table below I would have a new tab named "2206 - 6" and only data associated with that would remain, keeping in mind that this range of data will change each time the macro is used.
Before:
After:
Interval Number
2206 - 6
6304 - 5
4102 - 20
The table begins in row 11, but I need to retain all of the information above. I have an Advanced Filter Macro that gets close to what I want, but its doing two things I don't want: creating empty tabs and not retaining information above row 11.
Sub parse_data()
Dim lr As Long
Dim ws As Worksheet
Dim vcol, i As Integer
Dim icol As Long
Dim myarr As Variant
Dim title As String
Dim titlerow As Integer
vcol = 1
Set ws = Sheets("Offshore Searches")
lr = ws.Cells(ws.Rows.Count, vcol).End(xlUp).Row
title = "A11:G20"
titlerow = ws.Range(title).Cells(1).Row
icol = ws.Columns.Count
ws.Cells(1, icol) = "Unique"
For i = 2 To lr
On Error Resume Next
If ws.Cells(i, vcol) <> "" And _
Application.WorksheetFunction.Match(ws.Cells(i, vcol), ws.Columns(icol), 0) = 0 Then
ws.Cells(ws.Rows.Count, icol).End(xlUp).Offset(1) = ws.Cells(i, vcol)
End If
Next
myarr = Application.WorksheetFunction.Transpose(ws.Columns(icol).SpecialCells(xlCellTypeConstants))
ws.Columns(icol).Clear
For i = 2 To UBound(myarr)
ws.Range(title).AutoFilter field:=vcol, Criteria1:=myarr(i) & ""
If Not Evaluate("=ISREF('" & myarr(i) & "'!A1)") Then
Sheets.Add(after:=Worksheets(Worksheets.Count)).Name = myarr(i) & ""
Else
Sheets(myarr(i) & "").Move after:=Worksheets(Worksheets.Count)
End If
ws.Range("A" & titlerow & ":A" & lr).EntireRow.Copy Sheets(myarr(i) & "").Range("A1")
Sheets(myarr(i) & "").Columns.AutoFit
Next
ws.AutoFilterMode = False
ws.Activate
End Sub
I also have a macro which creates tabs based on a range without the advanced filter, so each tab looks identical (just the tab name changes)
Sub CreateWorkSheetByRange()
Dim WorkRng As Range
Dim ws As Worksheet
Dim arr As Variant
On Error Resume Next
Set WorkRng = Application.Selection
Set WorkRng = Application.InputBox("Range", xTitleId, WorkRng.Address, Type:=8)
arr = WorkRng.Value
Sheets("Offshore Searches").Select
Cells.Select
Selection.Copy
Application.ScreenUpdating = False
For i = 1 To UBound(arr, 1)
For j = 1 To UBound(arr, 2)
Set ws = Worksheets.Add(after:=Application.ActiveSheet)
ws.Name = arr(i, j)
ActiveSheet.Paste
Range("A1").Select
Next
Next
Application.ScreenUpdating = True
End Sub
Is there a way to both create tabs based on a range while simultaneously using an advanced filter?
Another option (tested)
All functions bellow, in a separate module
It copies the main sheet, deletes the button and uses auto filter to remove unneeded rows
This uses dictionaries and late binding is slow: CreateObject("Scripting.Dictionary")
Early binding is fast: VBA Editor -> Tools -> References -> Add Microsoft Scripting Runtime
Option Explicit
Private Const X As String = vbNullString
Public Sub CreateTabs()
Const FIRST_CELL As String = "Interval Number"
Const LAST_CELL As String = "Vesting Doc Number (LC/RS)"
Dim wb As Workbook, ws As Worksheet, wsNew As Worksheet, d As Dictionary, i As Long
Dim fr As Long, lr As Long, fc As Long, found As Range, rng As Range, val As String
SetDisplay False
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Offshore Searches")
Set found = FindCell(ws.UsedRange, FIRST_CELL)
If Not found Is Nothing Then
fr = found.Row + 1
fc = found.Column
End If
Set found = FindCell(ws.UsedRange, LAST_CELL)
If Not found Is Nothing Then lr = found.Row - 1
If fr > 0 And fc > 0 And lr >= fr Then
If Not ws.AutoFilter Is Nothing Then ws.UsedRange.AutoFilter
Set rng = ws.Range(ws.Cells(fr, fc), ws.Cells(lr, fc))
Dim arr As Variant, r As Long
arr = rng
Set d = New Dictionary
For r = 1 To UBound(arr)
val = Trim(CStr(arr(r, 1)))
val = CleanWsName(val)
If Not d.Exists(val) Then d.Add r, val
Next
For i = 1 To d.Count
If Not WsExists(d(i)) Then
ws.Copy After:=wb.Worksheets(wb.Worksheets.Count)
Set wsNew = wb.Worksheets(wb.Worksheets.Count)
With wsNew
.Name = d(i): If .Shapes.Count = 1 Then wsNew.Shapes.Item(1).Delete
Set rng = .Range(.Cells(fr - 1, fc), .Cells(lr, fc))
rng.AutoFilter Field:=1, Criteria1:="<>" & d(i), Operator:=xlAnd, Criteria2:="<>"
Set rng = rng.Offset(1).Resize(rng.Rows.Count - 1)
rng.SpecialCells(xlCellTypeVisible).EntireRow.Delete Shift:=xlUp
rng.AutoFilter
End With
End If
Next
End If
ws.Activate
SetDisplay True
End Sub
Public Sub SetDisplay(Optional ByVal status As Boolean = False)
Application.ScreenUpdating = status
Application.DisplayAlerts = status
End Sub
Public Function FindCell(ByRef rng As Range, ByVal celVal As String) As Range
Dim found As Range
If Not rng Is Nothing Then
If Len(celVal) > 0 Then
Set found = rng.Find(celVal, MatchCase:=True)
If Not found Is Nothing Then Set FindCell = found
End If
End If
End Function
Public Function CleanWsName(ByVal wsName As String) As String
Const x = vbNullString
wsName = Trim$(wsName) 'Trim, then remove [ ] / \ < > : * ? | "
wsName = Replace(Replace(Replace(wsName, "[", x), "]", x), " ", x)
wsName = Replace(Replace(Replace(wsName, "/", x), "\", x), ":", x)
wsName = Replace(Replace(Replace(wsName, "<", x), ">", x), "*", x)
wsName = Replace(Replace(Replace(wsName, "?", x), "|", x), Chr(34), x)
If Len(wsName) = 0 Then wsName = "DT " & Format(Now, "yyyy-mm-dd hh.mm.ss")
CleanWsName = Left$(wsName, 31) 'Resize to max len of 31
End Function
Public Function WsExists(ByVal wsName As String) As Boolean
Dim ws As Worksheet
With ThisWorkbook
For Each ws In .Worksheets
If ws.Name = wsName Then
WsExists = True
Exit Function
End If
Next
End With
End Function
Assumptions
Interval Numbers format is consistent: Unit & " - " & Week (=B12 & " - " & C12)
Interval Numbers are not longer than 31 character, and don't contain these special chars: [ ] / \ ? * .
If so, the sheet names will be shortened to 31 chars
and all special chars mentioned removed (Excel limitation for Sheet names)
Working row starts after cell "Interval Number" and stop before "Vesting Doc Number (LC/RS)"
There are no spaces before or after "Interval Number" and "Vesting Doc Number (LC/RS)"
Main tab name is exactly "Offshore Searches", and it contains only one button ("Create Tabs")
For what you have shown in the images, you may try something like this to achieve that...
Sub InsertSheets()
Dim sws As Worksheet, ws As Worksheet
Dim slr As Long, i As Long
Dim Rng As Range, Cell As Range
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Set sws = Sheets("Sheet1")
If sws.Range("A12").Value = "" Then
MsgBox "No Interval Numbers found on the sheet.", vbExclamation
Exit Sub
End If
slr = sws.Range("A11").End(xlDown).Row
Set Rng = sws.Range("A12:A" & slr)
For Each Cell In Rng
On Error Resume Next
Sheets(Cell.Value).Delete
On Error GoTo 0
sws.Copy after:=Sheets(Sheets.Count)
Set ws = ActiveSheet
ws.Name = Cell.Value
ws.DrawingObjects.Delete
With ws
For i = slr To 12 Step -1
If i <> Cell.Row Then ws.Rows(i).Delete
Next i
End With
Set ws = Nothing
Next Cell
sws.Activate
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

VBA to 'print' matched search results to new WS

I have some coding which loops through a list of policy numbers, and then searches through an entire workbook for a matching policy number, bolding any matches found in the original list. The coding works fine, however what I would like to do if possible, is to 'print' the address/cell location of the matching value. For example, if policy number 1 was located on sheet 3, cell a1, then this would be displayed on a new blank worksheet. I'm sure this can be done using dictionaries and the print.debug function, But I want to avoid using a dictionary if possible. Hope this makes sense!
Sub HighlightMatches()
Application.ScreenUpdating = True
Dim var As Variant, iSheet As Integer, iRow As Long, iRowL As Long,bln As Boolean
iRowL = Sheets(3).Cells(Rows.Count, "P").End(xlUp).row
For iRow = 1 To iRowL
If Not IsEmpty(Cells(iRow, 16)) Then
For iSheet = ActiveSheet.Index + 1 To Worksheets.Count
bln = False
var = Application.match(Cells(iRow, 16).Value, Worksheets(iSheet).Columns(16), 0)
If Not IsError(var) Then
bln = True
Exit For
End If
Next iSheet
End If
' The below would 'print' all matches and an offset value to a new Worksheet.
If bln = False Then
Cells(iRow, 16).Font.Bold = False
Else
Cells(iRow, 16).Font.Bold = True
End If
Next iRow
Application.ScreenUpdating = True
End Sub
This would do it:
Option Explicit
Sub HighlightMatches()
Dim OriginalWS As Worksheet, NewListWorksheet As Worksheet, ws As Worksheet
Dim Cel As Range, Rng As Range, rSearchCell As Range
Set OriginalWS = ActiveSheet
Set Rng = Columns("P:P").SpecialCells(xlCellTypeConstants, 23)
For Each Cel In Rng
For Each ws In ActiveWorkbook.Worksheets
If ws.Name <> OriginalWS.Name And ws.Name <> "Search List" Then
On Error Resume Next
Set rSearchCell = ws.Columns("P:P").Find(What:=Cel.Value, After:=ActiveCell, LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
On Error GoTo 0
If Not rSearchCell Is Nothing Then
Cel.Font.Bold = True
Sheets.Add(After:=Sheets(Sheets.Count)).Name = "Search List"
On Error Resume Next
Set NewListWorksheet = ActiveSheet
On Error GoTo 0
If Not NewListWorksheet Is Nothing Then
NewListWorksheet.Range("A" & NewListWorksheet.Range("A" & NewListWorksheet.Rows.Count).End(xlUp).Row + 1).Value = Cel.Value
Else
Set NewListWorksheet = Sheets.Add.Name = "Search List"
NewListWorksheet.Range("A1").Value = "Search results"
NewListWorksheet.Range("A1").Font.Bold = True
NewListWorksheet.Range("A2").Value = Cel.Value
End If
Else
Cel.Font.Bold = False
End If
End If
Set rSearchCell = Nothing 'Clear Cel
Next
Next
End Sub

Separate Excel rows into individual sheets and retain header

I am trying to use VBA in Excel to separate rows into separate sheets and retain headers. Below is what I have so far. It works except I get the header row, then the individual row I want to move to the sheet is there BUT it's there three times instead of one. I am basically going by trial and error and I am stumped. Help please! I have no experience with this:
Sub DispatchTimeSeriesToSheets()
Dim ws As Worksheet
Set ws = Sheets("Scoring")
Dim LastRow As Long
LastRow = Range("A" & ws.Rows.Count).End(xlUp).Row
' stop processing if we don't have any data
If LastRow < 2 Then Exit Sub
Application.ScreenUpdating = False
SortScoring LastRow, ws
CopyDataToSheets LastRow, ws
ws.Select
Application.ScreenUpdating = True
End Sub
Sub SortScoring(LastRow As Long, ws As Worksheet)
ws.Range("A4:W" & LastRow).Sort Key1:=ws.Range("A4"), Key2:=ws.Range("W4")
End Sub
Sub CopyDataToSheets(LastRow As Long, src As Worksheet)
Dim rng As Range
Dim cell As Range
Dim Series As String
Dim SeriesStart As Long
Dim SeriesLast As Long
Set rng = Range("A4:A" & LastRow)
SeriesStart = 2
Series = Range("A" & SeriesStart).Value
For Each cell In rng
If cell.Value <> Series Then
SeriesLast = cell.Row - 1
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
Series = cell.Value
SeriesStart = cell.Row
End If
Next
' copy the last series
SeriesLast = LastRow
CopySeriesToNewSheet src, SeriesStart, SeriesLast, Series
End Sub
Sub CopySeriesToNewSheet(src As Worksheet, Start As Long, Last As Long, _
name As String)
Dim tgt As Worksheet
If (SheetExists(name)) Then
MsgBox "Sheet " & name & " already exists. " _
& "Please delete or move existing sheets before" _
& " copying data from the Scoring.", vbCritical, _
"Time Series Parser"
End
End If
Worksheets.Add(After:=Worksheets(Worksheets.Count)).name = name
Set tgt = Sheets(name)
' copy header row from src to tgt
tgt.Range("A1:W1").Value = src.Range("A1:W1").Value
' copy data from src to tgt
tgt.Range("A4:W" & Last - Start + 2).Value = _
src.Range("A" & Start & ":W" & Last).Value
End Sub
Function SheetExists(name As String) As Boolean
Dim ws As Worksheet
SheetExists = True
On Error Resume Next
Set ws = Sheets(name)
If ws Is Nothing Then
SheetExists = False
End If
End Function
Try this:
Sub doitall()
Dim ows As Worksheet
Dim tws As Worksheet
Dim rng As Range
Dim cel As Range
Dim LastRow As Long
Dim tLastRow As Long
Set ows = Sheets("Scoring")
With ows
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set rng = .Range("A4:A" & LastRow)
For Each cel In rng
If Not SheetExists(cel.Value) Then
Set tws = Worksheets.Add(After:=Sheets(Worksheets.Count))
tws.Name = cel.Value
tws.Rows(1).Resize(3).Value = .Rows(1).Resize(3).Value
Else
Set tws = Sheets(cel.Value)
End If
tLastRow = tws.Range("A" & tws.Rows.Count).End(xlUp).Offset(1).Row
tws.Rows(tLastRow).Value = .Rows(cel.Row).Value
Next
End With
End Sub
Function SheetExists(SName As String, _
Optional ByVal WB As Workbook) As Boolean
On Error Resume Next
If WB Is Nothing Then Set WB = ActiveWorkbook
SheetExists = CBool(Len(WB.Sheets(SName).Name))
End Function
This will do what you are looking for
Const HeaderRow = 3
Sub MoveRecordsByValues()
Dim ws As Worksheet
Dim dws As Worksheet
Dim SheetName As String
Application.DisplayAlerts = False
For Each ws In Sheets
If ws.name <> "Scoring" Then ws.Delete
Next ws
Set ws = Sheets("Scoring")
StartRow = HeaderRow + 1
LastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
For RowCounter = StartRow To LastRow
SheetName = ws.Cells(RowCounter, 1)
If Not SheetExists(SheetName) Then SetUpSheet SheetName, ws, HeaderRow
Set dws = Worksheets(SheetName)
DestLastRow = dws.Range("A" & dws.Rows.Count).End(xlUp).Row + 1
ws.Rows(RowCounter).Copy dws.Cells(DestLastRow, 1)
Next RowCounter
Application.DisplayAlerts = True
End Sub
Function SheetExists(name As String) As Boolean
SheetExists = True
On Error GoTo errorhandler
Sheets(name).Activate
Exit Function
errorhandler:
SheetExists = False
End Function
Sub SetUpSheet(name, SourceSheet, HeaderRow)
Dim DestSheet As Worksheet
Set DestSheet = Sheets.Add
DestSheet.name = name
SourceSheet.Rows(1).Copy DestSheet.Cells(1, 1)
SourceSheet.Rows(2).Copy DestSheet.Cells(2, 1)
SourceSheet.Rows(3).Copy DestSheet.Cells(3, 1)
End Sub

Looping Filter Code through Directory

I have a code that does some advanced filters and creates a new sheet in the workbook. I need to add a code that can loop it through a directory and not miss any sheets.
Can anyone help with this? I've tried the generic ones online and just can't seem to get it to work on a workbook after the first one in the directory.
Sub Looper()
'a.t.v.5 + extra splitting of scen names(+,-,etc).
'looping dir
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -3).Value = ws.Name
r.Offset(0, -2).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -2).Value = ws.Name
y.Offset(0, -1).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
End If
End If
End With
Next ws
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
EDIT Aug 24
Sub looperv2()
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & _
intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
If (lngNextRow - lngStartRow) > 1 Then
z.Resize(lngNextRow - lngStartRow, 2).FillDown
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Here you go your code modified slightly:
Sub looperv2()
Dim wb As Workbook, fileNames As Object, errCheck As Boolean 'I have added this Sept 9, 2015
Dim ws As Worksheet, wks As Worksheet, wksSummary As Worksheet
Dim y As Range, intRow As Long, i As Integer
Dim r As Range, lr As Long, myrg As Range, z As Range
Dim boolWritten As Boolean, lngNextRow As Long
Dim intColNode As Integer, intColScenario As Integer
Dim intColNext As Integer, lngStartRow As Long
' Turn off screen updating and automatic calculation
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
' Create a new worksheet, if required
On Error Resume Next
Set wksSummary = ActiveWorkbook.Worksheets("Unique data")
On Error GoTo 0
If wksSummary Is Nothing Then
Set wksSummary = ActiveWorkbook.Worksheets.Add(After:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count))
wksSummary.Name = "Unique data"
End If
' Set the initial output range, and assign column headers
With wksSummary
Set y = .Cells(.Rows.Count, 3).End(xlUp).Offset(1, 0)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row.Range("A1:D1").Value = Array("File Name", "Sheet Name", "Node Name", "Scenario Name")
End With
'Get User input for files to search 'I added the below Sept 9, 2015
Set fileNames = CreateObject("Scripting.Dictionary") 'I added the below Sept 9, 2015
errCheck = UserInput.FileDialogDictionary(fileNames) 'I added the below Sept 9, 2015
If errCheck Then 'I added the below Sept 9, 2015
Exit Sub 'I added the below Sept 9, 2015
End If 'I added the below Sept 9, 2015
'''
For Each Key In fileNames 'loop through the dictionary I added the below Sept 9, 2015
Set wb = Workbooks.Open(fileNames(Key)) 'I added the below Sept 9, 2015
wb.Application.Visible = False 'make it not visible I added the below Sept 9, 2015
' Check each sheet in turn
For Each ws In ActiveWorkbook.Worksheets
With ws
' Only action the sheet if it's not the 'Unique data' sheet
If .Name <> wksSummary.Name Then
boolWritten = False
' Find the Scenario column
intColScenario = 0
On Error Resume Next
intColScenario = WorksheetFunction.Match("scenarioName", .Rows(1), 0)
On Error GoTo 0
If intColScenario > 0 Then
' Only action if there is data in column E
If Application.WorksheetFunction.CountA(.Columns(intColScenario)) > 1 Then
' Find the next free column, in which the extract formula will be placed
intColNext = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
' Assign formulas to the next free column to identify the scenario name to the left of the first _ character
.Cells(1, intColNext).Value = "Test"
lr = .Cells(.Rows.Count, intColScenario).End(xlUp).Row
Set myrg = .Range(.Cells(2, intColNext), .Cells(lr, intColNext))
With myrg
.ClearContents
.FormulaR1C1 = "=IFERROR(LEFT(RC" & intColScenario & ",FIND(INDEX({""+"",""-"",""_"",""$"",""%""},1,MATCH(1,--(ISNUMBER(FIND({""+"",""-"",""_"",""$"",""%""},RC" & _
intColScenario & "))),0)), RC" & intColScenario & ")-1), RC" & intColScenario & ")"
.Value = .Value
End With
' Copy unique values from the formula column to the 'Unique data' sheet, and write sheet & file details
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).AdvancedFilter xlFilterCopy, , r, True
r.Offset(0, -2).Value = ws.Name
r.Offset(0, -3).Value = ws.Parent.Name
' Clear the interim results
.Range(.Cells(1, intColNext), .Cells(lr, intColNext)).ClearContents
' Delete the column header copied to the list
r.Delete Shift:=xlUp
boolWritten = True
End If
End If
' Find the Node column
intColNode = 0
On Error Resume Next
intColNode = WorksheetFunction.Match("node", .Rows(1), 0)
On Error GoTo 0
If intColNode > 0 Then
' Only action if there is data in column A
If Application.WorksheetFunction.CountA(.Columns(intColNode)) > 1 Then
lr = .Cells(.Rows.Count, intColNode).End(xlUp).Row
' Copy unique values from column A to the 'Unique data' sheet, and write sheet & file details (if not already written)
.Range(.Cells(1, intColNode), .Cells(lr, intColNode)).AdvancedFilter xlFilterCopy, , y, True
If Not boolWritten Then
y.Offset(0, -1).Value = ws.Name
y.Offset(0, -2).Value = ws.Parent.Name
End If
' Delete the column header copied to the list
y.Delete Shift:=xlUp
End If
End If
' Identify the next row, based on the most rows used in columns C & D
lngNextRow = WorksheetFunction.Max(wksSummary.Cells(wksSummary.Rows.Count, 3).End(xlUp).Row, wksSummary.Cells(wksSummary.Rows.Count, 4).End(xlUp).Row) + 1
If (lngNextRow - lngStartRow) > 1 Then
z.Resize(lngNextRow - lngStartRow, 2).FillDown
End If
Set y = wksSummary.Cells(lngNextRow, 3)
Set r = y.Offset(0, 1)
Set z = y.Offset(0, -2)
lngStartRow = y.Row
End If
End With
Next ws
wb.Application.Visible = True '' I added this Sept 9, 2015
wb.Close savechanges:=False ' I added this Sept 9, 2015
Set wb = Nothing 'release the object ' I added this Sept 9, 2015
Next 'End of the fileNames loop ' I added this Sept 9, 2015
Set fileNames = Nothing ' I added this Sept 9, 2015
' Autofit column widths of the report
wksSummary.Range("A1:D1").EntireColumn.AutoFit
' Reset system settings
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
and my file dialog code which I reused because it was already written. If you want to use a folder location you can use the file dialog folder picker option. Then just use a dictionary and loop through all files in the directory I suggest using the dir function and test for .xls or something like that.
Function FileDialogDictionary(ByRef file As Object) As Boolean ' returns true if the user cancels
'Declare a variable as a FileDialog object.
Dim fd As FileDialog
Dim item As Variant
Dim i As Long
'Create a FileDialog object as a File Picker dialog box.
file.RemoveAll 'clear the dictionary
Set fd = Application.FileDialog(msoFileDialogFilePicker)
'Declare a variable to contain the path
'of each selected item. Even though the path is a String,
'the variable must be a Variant because For Each...Next
'routines only work with Variants and Objects.
'Use a With...End With block to reference the FileDialog object.
With fd
'Use the Show method to display the File Picker dialog box and return the user's action.
'The user pressed the action button.
.Title = "Select Excel Workbooks" 'Change this to suit your purpose
.AllowMultiSelect = True
.Filters.Clear
.Filters.Add "Microsoft Excel files", "*.xlsx,*.xls"
If .Show = -1 Then
'Step through each string in the FileDialogSelectedItems collection.
For Each item In .SelectedItems 'loop through all selected and add to dictionary
i = i + 1
file.Add i, item
Next item
FileDialogDictionary = False
'The user pressed Cancel.
Else
FileDialogDictionary = True
Set fd = Nothing
Exit Function
End If
End With
Set fd = Nothing 'Set the object variable to Nothing.
End Function
Something like this would work I think:
Dim incomingFolderPath = "YOUR DIRECTORY HERE"
Dim archiveFolderPath As String = "Archive directory here"
While Directory.GetFiles(incomingFolderPath).Length > 0
Dim myFile as string = Dir(incomingFolderPath & "\*.*")
Dim fileToOpen As String = incomingFolderPath + myFile
'Logic here
System.IO.File.Move(fileToOpen, archiveFolderPath)
End While
The idea is it would check to see if the folder has anything in it, if it does it would use your logic then move that file to another location. It will loop through this until all files are moved.
Not sure if this is exactly what you're after but it should help.