Excel VBA Autofilter range fails - excel

I am trying to create multiple workbooks from one workbook. Each new workbook contains data from the main workbooks table, filtered by key. I used the code from here:
Q: How can I split data into multiple workbooks/files based on column in Excel?
but I had to do some minor adjustments, basically change starting rows / columns. Now, my code fails at the step "srg.AutoFilter sCol, Key" with the error message "AutoFilter method of Range class failed.
This is my code:
Sub ExportToWorkbooks()
Const aibPrompt As String = "Which column would you like to filter by?"
Const aibtitle As String = "Filter Column"
Const aibDefault As Long = 3
Dim dFileExtension As String: dFileExtension = ".xlsx"
Dim dFileFormat As XlFileFormat: dFileFormat = xlOpenXMLWorkbook
Dim dFolderPath As String: dFolderPath = "C:\Test\"
If Right(dFolderPath, 1) <> "\" Then dFolderPath = dFolderPath & "\"
' If Len(Dir(dFolderPath, vbDirectory)) = 0 Then Exit Sub ' folder not found
If Left(dFileExtension, 1) <> "." Then dFileExtension = "." & dFileExtension
Application.ScreenUpdating = False
Dim sCol As Variant
sCol = 2
If Len(CStr(sCol)) = 0 Then Exit Sub ' no entry
If sCol = False Then Exit Sub ' canceled
Dim sws As Worksheet: Set sws = ActiveSheet
If sws.FilterMode Then sws.ShowAllData
Dim srg As Range: Set srg = sws.Range("A10").CurrentRegion
Dim srCount As Long: srCount = srg.Rows.Count
If srCount < 3 Then Exit Sub ' not enough rows
Dim srrg As Range: Set srrg = srg.Rows(10) ' to copy column widths
Dim scrg As Range: Set scrg = srg.Columns(sCol)
Dim scData As Variant: scData = scrg.Value
' Write the unique values from the 1st column to a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case insensitive
Dim Key As Variant
Dim r As Long
For r = 11 To srCount
Key = scData(r, 1)
Debug.Print Key
If Not IsError(Key) Then ' exclude error values
If Len(Key) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next r
If dict.Count = 0 Then Exit Sub ' only error values and blanks
Erase scData
Dim dwb As Workbook
Dim dws As Worksheet
Dim dfcell As Range
Dim dFilePath As String
For Each Key In dict.Keys
' Add a new (destination) workbook and reference the first cell.
Set dwb = Workbooks.Add(xlWBATWorksheet) ' one worksheet
Set dws = dwb.Worksheets(1)
Set dfcell = dws.Range("A1")
' Copy/Paste
srrg.Copy
dfcell.PasteSpecial xlPasteColumnWidths
srg.AutoFilter sCol, Key
srg.SpecialCells(xlCellTypeVisible).Copy dfcell
sws.ShowAllData
dfcell.Select
' Save/Close
dFilePath = dFolderPath & Key & dFileExtension ' build the file path
Application.DisplayAlerts = False ' overwrite without confirmation
dwb.SaveAs dFilePath, xlOpenXMLWorkbook
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Next Key
sws.AutoFilterMode = False
Application.ScreenUpdating = True
MsgBox "Data exported.", vbInformation
End Sub
My table starts in cell A10.
Can anyone point me into the right direction?

It might not help you after all, but what you can do without need to figure this code out is simply delete top rows from new Workbooks, assuming the data is blank at top 9 rows because of your A10 start table.

Related

Follow up to splitting a sheet into multiple workbooks

I am trying to modify this code found here.
The code works great, but want to know how to enter column letter instead of number.
I believe it has something to do with this line
sCol = Application.InputBox(aibPrompt, aibtitle, aibDefault, , , , , 1)
but not able to get it to work.
Here is the code as found on the other page.
Option Explicit
Sub ExportToWorkbooks()
Const aibPrompt As String = "Which column would you like to filter by?"
Const aibtitle As String = "Filter Column"
Const aibDefault As Long = 3
Dim dFileExtension As String: dFileExtension = ".xlsx"
Dim dFileFormat As XlFileFormat: dFileFormat = xlOpenXMLWorkbook
Dim dFolderPath As String: dFolderPath = "C:\Users\WalteR01\Desktop\VPN Revalidations\Split by Manager\"
If Right(dFolderPath, 1) <> "\" Then dFolderPath = dFolderPath & "\"
If Len(Dir(dFolderPath, vbDirectory)) = 0 Then Exit Sub ' folder not found
If Left(dFileExtension, 1) <> "." Then dFileExtension = "." & dFileExtension
Application.ScreenUpdating = False
Dim sCol As Variant
sCol = Application.InputBox(aibPrompt, aibtitle, aibDefault, , , , , 1)
If Len(CStr(sCol)) = 0 Then Exit Sub ' no entry
If sCol = False Then Exit Sub ' canceled
Dim sws As Worksheet: Set sws = ActiveSheet
If sws.FilterMode Then sws.ShowAllData
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion
Dim srCount As Long: srCount = srg.Rows.Count
If srCount < 3 Then Exit Sub ' not enough rows
Dim srrg As Range: Set srrg = srg.Rows(1) ' to copy column widths
Dim scrg As Range: Set scrg = srg.Columns(sCol)
Dim scData As Variant: scData = scrg.Value
' Write the unique values from the 1st column to a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case insensitive
Dim Key As Variant
Dim r As Long
For r = 2 To srCount
Key = scData(r, 1)
If Not IsError(Key) Then ' exclude error values
If Len(Key) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next r
If dict.Count = 0 Then Exit Sub ' only error values and blanks
Erase scData
Dim dwb As Workbook
Dim dws As Worksheet
Dim dfcell As Range
Dim dFilePath As String
Dim DateText As String: DateText = Format(Date, "_mm_yyyy")
For Each Key In dict.Keys
' Add a new (destination) workbook and reference the first cell.
Set dwb = Workbooks.Add(xlWBATWorksheet) ' one worksheet
Set dws = dwb.Worksheets(1)
Set dfcell = dws.Range("A1")
' Copy/Paste
srrg.Copy
dfcell.PasteSpecial xlPasteColumnWidths
srg.AutoFilter sCol, Key
srg.SpecialCells(xlCellTypeVisible).Copy dfcell
sws.ShowAllData
dfcell.Select
' Save/Close
dFilePath = dFolderPath & Key & DateText & dFileExtension ' build the file path
Application.DisplayAlerts = False ' overwrite without confirmation
dwb.SaveAs dFilePath, xlOpenXMLWorkbook
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Next Key
sws.AutoFilterMode = False
Application.ScreenUpdating = True
MsgBox "Data exported.", vbInformation
End Sub
I have tried to change the line as stated but no luck. The application inbox is what I need to update but not sure how. This is the page I have been using https://learn.microsoft.com/en-us/office/vba/api/excel.application.inputbox
Dim sCol As Variant
sCol = Application.InputBox(aibPrompt, aibtitle, aibDefault, , , , , 1)
As documented here: https://learn.microsoft.com/en-us/office/vba/api/excel.application.inputbox#:~:text=The%20following%20table%20lists%20the%20values%20that%20can%20be%20passed%20in%20the%20Type%20argument
The last argument controls what type of value(s) can be accepted by the InputBox: 1 = Numeric so you need to swap that out for 2 (Text)
sCol = Application.InputBox(aibPrompt, aibtitle, aibDefault, , , , , 2)
OK now I read down... When you call srg.AutoFilter sCol, Key the first argument to AutoFilter should be the column number in the range to be filtered to which the filter should be applied.
Note if your table doesn't start in ColA there needs to be some adjustment, but if it does you could use Cells(1, sCol).Column to convert your column letter, so try:
srg.AutoFilter sws.Cells(1, sCol).Column, Key

Create multiple excel files keeping only specific values in column A from a master sheet

I am really struggling in creating a macro that from a master Excel file creates multiple Excel files based on the values in the first column. More specifically, I have in column "A" some categories, and based on all the categories (ITT1, ITT2, ITT3, ITT4 and ITT5) I would like to create multiple excel files containing the sheet with just 1 category. At the moment, I have been able to save just 1 file with 1 category. But I cannot do it with multiple. Could you kindly help me please? I am stuck.
Sub Split()
Dim location As String
location = "Z:\Incent_2022\ORDINARIA\RETAIL-WHS\Andamento\Q4\Andamento\Novembre\And. Inc Q4_ITT1.xlsm"
ActiveWorkbook.SaveAs Filename:=location, FileFormat:=52
With ActiveSheet
Const FirstRow As Long = 6
Dim LastRow As Long
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row ' get last used row in column A
Dim Row As Long
For Row = LastRow To FirstRow Step -1
If Not .Range("A" & Row).Value = "ITT1" Then
.Range("A" & Row).EntireRow.Delete
End If
Next Row
End With
ActiveWorkbook.Close SaveChanges:=True
End Sub
This is working for me perfectly. There are a few things you will need to change to fit your sheet.
Option Explicit
Sub Export_Files()
Dim I As Long
Dim lRow As Long
Dim SaveLoc As String
Dim OutWB As Workbook
Dim TypeList
Dim TypeRG As Range
' > Create Unique List of Used Types
lRow = Range("A" & Rows.Count).End(xlUp).Row
Set TypeRG = Sheet1.Range("A2:A" & lRow)
TypeList = Application.WorksheetFunction.Unique(TypeRG)
' > My Directory
SaveLoc = "C:\Users\cameron\Documents\temp\"
' >
For I = 1 To UBound(TypeList, 1)
'Create File:
Set OutWB = Workbooks.Add
OutWB.SaveAs SaveLoc & TypeList(I, 1)
'Transfer Data to file:
Sheet1.Range("A1:E" & lRow).AutoFilter Field:=1, Criteria1:=TypeList(I, 1)
Sheet1.Range("A1:E" & lRow).SpecialCells(xlCellTypeVisible).Copy
OutWB.Worksheets(1).Paste
OutWB.Save
OutWB.Close
Next I
End Sub
To Change:
SaveLoc - to your preferred directory
The TypeRG range if yours is not in A Column (also your lRow maybe)
your autofilter range if your data range is larger than mine.
Exaple of my data:
Export Split Data
Sub ExportSplitData()
' Define constants.
Const SRC_NAME As String = "Sheet1"
Const SRC_FIRST_CELL As String = "A5"
Const SRC_CRITERIA_COLUMN As Long = 1
Const DST_FOLDER As String _
= "Z:\Incent_2022\ORDINARIA\RETAIL-WHS\Andamento\Q4\Andamento\Novembre\"
Const DST_NAME_LEFT As String = "And. Inc Q4_"
Const DST_EXTENSION As String = ".xlsm"
' Reference the Source worksheet.
Dim swb As Workbook: Set swb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = swb.Sheets(SRC_NAME)
Application.ScreenUpdating = False
' To leave the source workbook intact, export the worksheet
' to a new (helper) workbook and reference the range (there).
sws.Copy
Dim hwb As Workbook: Set hwb = Workbooks(Workbooks.Count)
Dim hws As Worksheet: Set hws = hwb.Sheets(SRC_NAME)
If hws.FilterMode Then hws.ShowAllData
Dim hfCell As Range: Set hfCell = hws.Range(SRC_FIRST_CELL)
Dim hrg As Range, hdrg As Range, hfrrg As Range, hrCount As Long
With hws.UsedRange
Set hfrrg = Intersect(hfCell.EntireRow, .Cells)
Set hrg = hfrrg.Resize(.Rows.Count + .Row - hfrrg.Row)
hrCount = hrg.Rows.Count
Set hdrg = hrg.Resize(hrCount - 1).Offset(1) ' no headers
End With
' Sort the range by the criteria column.
hrg.Sort hrg.Columns(SRC_CRITERIA_COLUMN), xlAscending, , , , , , xlYes
' Write the unique values from the criteria column to a dictionary.
Dim hData() As Variant: hData = hdrg.Columns(SRC_CRITERIA_COLUMN).Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim r As Long
For r = 1 To hrCount - 1
If Len(CStr(hData(r, 1))) > 0 Then
dict(hData(r, 1)) = Empty
End If
Next r
' Loop through the keys of the dictionary and export
' the sorted helper worksheet to be processed in yet another file,
' the destination workbook.
Dim dwb As Workbook, dws As Worksheet, drg As Range, ddrg As Range
Dim rKey As Variant, dFilePath As String
For Each rKey In dict.Keys
hws.Copy
Set dwb = Workbooks(Workbooks.Count)
Set dws = dwb.Sheets(SRC_NAME)
Set drg = dws.Range(hrg.Address) ' has headers
Set ddrg = dws.Range(hdrg.Address) ' no headers
drg.AutoFilter SRC_CRITERIA_COLUMN, "<>" & rKey ' filter
ddrg.SpecialCells(xlCellTypeVisible).Delete xlShiftUp ' delete
dws.AutoFilterMode = False ' turn off filter
dFilePath = DST_FOLDER & DST_NAME_LEFT & rKey & DST_EXTENSION
Application.DisplayAlerts = False
dwb.SaveAs dFilePath, xlOpenXMLWorkbookMacroEnabled
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Next rKey
' Close the helper file.
hwb.Close SaveChanges:=False
Application.ScreenUpdating = True
' Inform.
MsgBox "Split data exported.", vbInformation
End Sub

Q: How can I split data into multiple workbooks/files based on column in Excel?

I'm very new to VBA so I hope I don't sound too ignorant. Each month I receive a report that contains data in the ranges A:T and about 7000-10000 rows. I need to separate this data into multiple workbooks/files so that I can send them out.
Currently, I manually filter the column and copy + paste the data into a blank excel and save as for each name but that is just insanely inefficient. I'm completely new to VBA or any sort of code so I've been scouring all over to find any that might help. I'm not sure if I can directly filter the data and save them into new workbooks but I am aware that you can do it as worksheets instead. I've come close using code from here: https://www.excelhow.net/split-data-into-multiple-worksheets-based-on-column.html as shown below but I've noticed that the character limitation of worksheet names causes some issues.
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
'This macro splits data into multiple worksheets based on the variables on a column found in Excel.
'An InputBox asks you which columns you'd like to filter by, and it just creates these worksheets.
Application.ScreenUpdating = False
vcol = Application.InputBox(prompt:="Which column would you like to filter by?", title:="Filter column", Default:="3", Type:=1)
Set ws = ActiveSheet
lr = ws.Cells(ws.Rows.Count, vcol).End(xlUp).Row
title = "A1"
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
Application.ScreenUpdating = True
End Sub
I was wondering if anyone can help me split the data into multiple workbooks based on the Name column (C) or bypass/avoid the character limitation that the worksheet name has, save the worksheets as separate workbooks and rename them later? The files that I send out have the name in the title (eg. NameXYZ_report) so preferably the outcome would have it named, based on the column as well.
Summary of the questions:
Split data into multiple workbooks directly based on Name (Column C value, often exceeds 31 characters) with the file name as ‘Name_report0122’ while keeping header (row 1)
Keep column width of original data
Edit; If it's not possible to save them directly as workbooks, would it be possible to save them as worksheets in a shortened form of the names, save those worksheets as workbooks and then mass rename the files properly afterwards?
I apologise for any confusion caused by my questions as I am new to this but I want to improve. Thank you all!
Export Filtered Data to a New Workbook
Option Explicit
Sub ExportToWorkbooks()
Const aibPrompt As String = "Which column would you like to filter by?"
Const aibtitle As String = "Filter Column"
Const aibDefault As Long = 3
Dim dFileExtension As String: dFileExtension = ".xlsx"
Dim dFileFormat As XlFileFormat: dFileFormat = xlOpenXMLWorkbook
Dim dFolderPath As String: dFolderPath = "C:\Test\"
If Right(dFolderPath, 1) <> "\" Then dFolderPath = dFolderPath & "\"
If Len(Dir(dFolderPath, vbDirectory)) = 0 Then Exit Sub ' folder not found
If Left(dFileExtension, 1) <> "." Then dFileExtension = "." & dFileExtension
Application.ScreenUpdating = False
Dim sCol As Variant
sCol = Application.InputBox(aibPrompt, aibtitle, aibDefault, , , , , 1)
If Len(CStr(sCol)) = 0 Then Exit Sub ' no entry
If sCol = False Then Exit Sub ' canceled
Dim sws As Worksheet: Set sws = ActiveSheet
If sws.AutoFilterMode Then sws.AutoFilterMode = False ' turn off AutoFilter
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion
Dim srCount As Long: srCount = srg.Rows.Count
If srCount < 3 Then Exit Sub ' not enough rows
Dim srrg As Range: Set srrg = srg.Rows(1) ' to copy column widths
Dim scrg As Range: Set scrg = srg.Columns(sCol)
Dim scData As Variant: scData = scrg.Value
' Write the unique values from the 1st column to a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case insensitive
Dim Key As Variant
Dim r As Long
For r = 2 To srCount
Key = scData(r, 1)
If Not IsError(Key) Then ' exclude error values
If Len(Key) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next r
If dict.Count = 0 Then Exit Sub ' only error values and blanks
Erase scData
Dim dwb As Workbook
Dim dws As Worksheet
Dim dfcell As Range
Dim dFilePath As String
For Each Key In dict.Keys
' Add a new (destination) workbook and reference the first cell.
Set dwb = Workbooks.Add(xlWBATWorksheet) ' one worksheet
Set dws = dwb.Worksheets(1)
Set dfcell = dws.Range("A1")
' Copy/Paste
srrg.Copy
dfcell.PasteSpecial xlPasteColumnWidths
srg.AutoFilter sCol, Key
srg.SpecialCells(xlCellTypeVisible).Copy dfcell
sws.ShowAllData
dfcell.Select
' Save/Close
dFilePath = dFolderPath & Key & dFileExtension ' build the file path
Application.DisplayAlerts = False ' overwrite without confirmation
dwb.SaveAs dFilePath, xlOpenXMLWorkbook
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Next Key
sws.AutoFilterMode = False
Application.ScreenUpdating = True
MsgBox "Data exported.", vbInformation
End Sub

Filter Range Copy Paste the Value and Create new Sheets

I have been trying to find an way to create multiple sheets using Specific Column data.
If Col"A" has multiple duplicate entries then filter single value create the new sheet using that value name, copy all the data and paste into newly added sheet.
I am unable to elaborate this thing in words and sorry for my poor English, i have attached an example workbook.
Where Sheet1 has data using Column A code will create multiple sheets. Your help will be much appreciated.
Sub CopyPartOfFilteredRange()
Dim src As Worksheet
Dim tgt As Worksheet
Dim filterRange As Range
Dim copyRange As Range
Dim lastRow As Long
Set src = ThisWorkbook.Sheets("Sheet1")
Set tgt = ThisWorkbook.Sheets("Sheet8")
src.AutoFilterMode = False
lastRow = src.Range("A" & src.Rows.Count).End(xlUp).Row
Set filterRange = src.Range("A1:A" & lastRow)
Set copyRange = src.Range("A1:P" & lastRow)
filterRange.AutoFilter field:=1, Criteria1:="CC"
copyRange.SpecialCells(xlCellTypeVisible).Copy tgt.Range("A1")
End Sub
Data Sheet
CC New Sheet
DD New Sheet
Till the last value HH
Please, test the next adapted code:
Sub CopyPartOfFilteredRange()
Dim src As Worksheet, tgt As Worksheet, copyRange As Range, filterRange As Range, lastRow As Long
Dim dict As Object, filterArr, i As Long
Set src = ActiveSheet ' ActiveWorkbook.Sheets("Sheet1")
lastRow = src.Range("A" & src.rows.count).End(xlUp).row
Set copyRange = src.Range("A1:P" & lastRow)
Set filterRange = src.Range("A2:A" & lastRow) 'it assumes that there are headers on the first row
filterArr = filterRange.value 'place it in an array for faster iteration
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To UBound(filterArr)
If filterArr(i, 1) <> "" Then dict(filterArr(i, 1)) = 1 'extract uniques strings
Next
filterArr = dict.Keys 'unique strings to be used in filterring
'some optimization:
With Application
.ScreenUpdating = False
.EnableEvents = False
.Calculation = xlCalculationManual
End With
For i = 0 To UBound(filterArr)
src.AutoFilterMode = False
'insert the new sheet and name it as filterr criteria, or use the existing one, if any:
On Error Resume Next
Set tgt = ActiveWorkbook.Sheets(left(filterArr(i), 31))
If err.Number = 0 Then 'if sheets already exists:
tgt.cells.Clear 'clear its content and use it
Else 'if not, insert and name it:
Set tgt = ActiveWorkbook.Sheets.Add(After:=src)
If Len(filterArr(i)) > 31 Then filterArr(i) = left(filterArr(i), 31)
tgt.Name = filterArr(i): err.Clear
End If
On Error GoTo 0
filterRange.AutoFilter field:=1, Criteria1:=filterArr(i)
copyRange.SpecialCells(xlCellTypeVisible).Copy tgt.Range("A1")
Next i
src.AutoFilterMode = False
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = xlCalculationAutomatic
End With
MsgBox "Processed " & UBound(filterArr) & "PCP Provider Names..."
End Sub
The above code has been updated to process the active sheet (and sheets on active workbook).
It needs a little optimization (`ScreenUpdating`, `EnableEvents`, `Calculation`) and check if the sheet with a specific name already exists, clearing all (in such a case) and reuse it, instead of inserting a new one.
There is a lot going on here:
You want sheets named with the duplicate values in column A. First, you need the unique values, which you can find using the Unique function: https://support.microsoft.com/en-us/office/unique-function-c5ab87fd-30a3-4ce9-9d1a-40204fb85e1e
You need to pass those values into an array and then loop through each: https://www.automateexcel.com/vba/loop-through-array/
Then you need to copy the values and paste to each new sheet which can be done with the autofilter and usedrange.
Then you need a lot error handling for sheets added or deleted.
Try this solution:
Sub CopyPartOfFilteredRange()
Application.ScreenUpdating = False
Dim i As Long
Dim LastRow As Long
Dim UValues As Variant
Dim myrange As Range
Dim sht As Worksheet
Dim list As New Collection
Set sht = ThisWorkbook.Sheets(1)
On Error Resume Next
LastRow = sht.Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
If LastRow = 0 Then
MsgBox "Worksheet contains no data"
Application.ScreenUpdating = True
End
End If
On Error GoTo 0
Set myrange = sht.Range("A2:A" & LastRow)
On Error Resume Next
For Each Value In myrange
list.Add CStr(Value), CStr(Value) 'extract unique strings
Next
On Error GoTo 0
ReDim UValues(list.Count - 1, 0)
For i = 0 To list.Count - 1
UValues(i, 0) = list(i + 1)
Next
For i = LBound(UValues) To UBound(UValues)
If Len(UValues(i, 0)) = 0 Then
GoTo Nexti
Else
On Error Resume Next
ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)).Name = UValues(i, 0)
If Err.Number = "1004" Then
On Error GoTo 0
Application.DisplayAlerts = False
MsgBox "Worksheet name " & UValues(i, 0) & " already taken"
ActiveSheet.Delete
Application.DisplayAlerts = True
GoTo Nexti
Else
On Error GoTo 0
sht.AutoFilterMode = False
sht.UsedRange.AutoFilter Field:=1, Criteria1:=UValues(i, 0), VisibleDropDown:=False, Operator:=xlFilterValues
sht.UsedRange.SpecialCells(xlCellTypeVisible).Copy
With ThisWorkbook.Sheets(UValues(i, 0))
.Range("A1").PasteSpecial ''Set this to appropriate sheet number
End With
Application.CutCopyMode = False
End If
End If
Nexti:
Next i
sht.AutoFilterMode = False
Application.ScreenUpdating = True
End Sub
Create Unique Worksheets
This will delete each possibly existing sheet before it will add a new worksheet and copy the filtered data to it.
If a worksheet name exceeds the 31 character limit, its name will be truncated.
If a worksheet name is invalid, it will not be renamed.
The Solution
Option Explicit
Sub CopyUniqueWorksheets()
Const ProcTitle As String = "Copy Unique Worksheets"
Dim dTime As Double: dTime = Timer ' time measuring
Debug.Print "Started '" & ProcTitle & "' at '" & Now & "'." ' log
Const swsName As String = "Sheet1"
Const sCol As Long = 1
Const dFirstCellAddress As String = "A1"
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets(swsName)
If sws.AutoFilterMode Then sws.AutoFilterMode = False
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion ' Source Range
Dim srCount As Long: srCount = srg.Rows.Count ' Source Rows Count
If srCount < 2 Then Exit Sub ' just headers or no data at all
Dim sData As Variant: sData = srg.Columns(sCol).Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim dKey As Variant
Dim dString As String
Dim r As Long
' Write the unique strings to a dictionary.
For r = 2 To srCount
dKey = sData(r, 1)
If Not IsError(dKey) Then
If Len(dKey) > 0 Then
dString = CStr(dKey)
If StrComp(dString, swsName, vbTextCompare) <> 0 Then
dict(dString) = Empty
End If
End If
End If
Next r
If dict.Count = 0 Then Exit Sub ' only blanks and error values and whatnot
Erase sData
Application.ScreenUpdating = False
Dim scrg As Range ' Source Copy Range
Dim dws As Object
Dim dwsName As String
For Each dKey In dict.Keys
' Restrict to maximum allowed characters (31).
dwsName = dKey
If Len(dwsName) > 31 Then
dwsName = Left(dwsName, 31)
Debug.Print "'" & dKey & "' is too long." & vbLf _
& "'" & dwsName & "' is used in the continuation." ' log
End If
' Delete possibly existing sheet.
On Error Resume Next
Set dws = wb.Sheets(dwsName)
On Error GoTo 0
If Not dws Is Nothing Then ' destination sheet exists
Application.DisplayAlerts = False
dws.Delete
Application.DisplayAlerts = True
'Else ' destination sheet doesn't exist
End If
' Create a reference to a newly added (destination) worksheet.
Set dws = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
' Rename Destination Worksheet.
On Error Resume Next
dws.Name = dwsName
If Err.Number <> 0 Then ' invalid sheet name
' log
Debug.Print "'" & dwsName & "' cannot be used as a sheet name."
'Else ' valid sheet name
End If
On Error GoTo 0
' Create a reference to the Source Copy Range.
srg.AutoFilter sCol, dKey
Set scrg = srg.SpecialCells(xlCellTypeVisible) ' headers are visible
sws.AutoFilterMode = False
' Copy the Source Copy Range to the Destination Worksheet.
scrg.Copy dws.Range(dFirstCellAddress)
' Initialize Destination Worksheet variable (reference).
Set dws = Nothing
Next dKey
sws.Activate
Application.ScreenUpdating = True
Debug.Print "It took " & Timer - dTime & " seconds." ' time measuring
Debug.Print "Ended '" & ProcTitle & "' at '" & Now & "'." ' log
MsgBox "Unique worksheets created.", vbInformation, ProcTitle
End Sub
Barely Related
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a workbook ('wb'), deletes all sheets except the ones
' whose names are in a list ('ExceptionsList').
' Remarks: At least one of the remaining sheets has to be visible.
' A very hidden sheet cannot be deleted.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub DeleteSheets()
On Error GoTo ClearError
Const ExceptionsList As String = "Sheet1"
Const Delimiter As String = "," ' tied to 'ExceptionsList'
Dim wb As Workbook: Set wb = ActiveWorkbook
Dim Exceptions() As String: Exceptions = Split(ExceptionsList, Delimiter)
Dim sh As Object
Dim ex As Long
Dim IsFoundVisibleSheet
For ex = 0 To UBound(Exceptions)
On Error Resume Next
Set sh = Nothing
Set sh = wb.Sheets(Exceptions(ex))
On Error GoTo ClearError
If Not sh Is Nothing Then ' sheet exists
If sh.Visible = xlSheetVisible Then ' sheet is visible
IsFoundVisibleSheet = True
Exit For
'Else ' sheet is not visible
End If
'Else ' sheet doesn't exist
End If
Next ex
If Not IsFoundVisibleSheet Then Exit Sub ' no remaining visible sheets
Dim SheetNames() As String: ReDim SheetNames(1 To wb.Sheets.Count)
Dim VeryHidden() As String: ReDim VeryHidden(1 To wb.Sheets.Count)
Dim sn As Long
Dim vh As Long
Dim shName As String
For Each sh In wb.Sheets
shName = sh.Name
If IsError(Application.Match(shName, Exceptions, 0)) Then
sn = sn + 1
SheetNames(sn) = shName
If sh.Visible = xlVeryHidden Then
vh = vh + 1
VeryHidden(vh) = shName
'Else ' sheet is not very hidden
End If
'Else ' sheet found in 'Exceptions'
End If
Next sh
If sn = 0 Then Exit Sub ' no sheets to delete
ReDim Preserve SheetNames(1 To sn)
If vh > 0 Then
ReDim Preserve VeryHidden(1 To vh)
For vh = 1 To vh
wb.Sheets(VeryHidden(vh)).Visible = xlSheetVisible
Next vh
'Else ' no very hidden sheets
End If
Application.DisplayAlerts = False ' delete without confirmation
wb.Sheets(SheetNames).Delete
Application.DisplayAlerts = True
ProcExit:
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume ProcExit
End Sub
Initial (Old) Answer
The idea is valid, but it takes forever on OP's data.
This will delete each possibly existing sheet before copying the source worksheet and renaming it. Then it will filter it to delete the undesired rows (not entire rows) of the table range in the copied worksheet.
Option Explicit
Sub CopyUniqueWorksheets()
Const swsName As String = "Sheet1"
Const sCol As Long = 1
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets(swsName)
If sws.AutoFilterMode Then sws.AutoFilterMode = False
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion ' Table Range
Dim scrg As Range: Set scrg = srg.Columns(sCol) ' Column Range
Dim srCount As Long: srCount = scrg.Rows.Count
Dim dcrgAddress As String: dcrgAddress = scrg.Address(0, 0)
Dim sdrg As Range: Set sdrg = srg.Resize(srCount - 1).Offset(1) ' Data Range
Dim ddrgAddress As String: ddrgAddress = sdrg.Address(0, 0)
If srCount < 2 Then Exit Sub ' just headers or no data at all
Dim sData As Variant: sData = scrg.Value
Dim drgAddress As String: drgAddress = srg.Address(0, 0)
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim dKey As Variant
Dim dString As String
Dim r As Long
For r = 2 To srCount
dKey = sData(r, 1)
If Not IsError(dKey) Then
If Len(dKey) > 0 Then
dString = CStr(dKey)
If StrComp(dString, swsName, vbTextCompare) <> 0 Then
dict(dString) = Empty
End If
End If
End If
Next r
Application.ScreenUpdating = False
Dim dws As Object
Dim drg As Range ' Delete Range
Dim dcrg As Range ' Column Range
Dim ddrg As Range ' Data Range
For Each dKey In dict.Keys
' Delete possibly existing sheet.
On Error Resume Next
Set dws = wb.Sheets(dKey)
On Error GoTo 0
If Not dws Is Nothing Then ' destination sheet exists
Application.DisplayAlerts = False
dws.Delete
Application.DisplayAlerts = True
'Else ' destination sheet doesn't exist
End If
' Copy source worksheet.
sws.Copy After:=wb.Sheets(wb.Sheets.Count)
Set dws = ActiveSheet
' Rename destination worksheet.
On Error Resume Next
dws.Name = dKey
If Err.Number <> 0 Then
MsgBox "'" & dKey & "' is an invalid sheet name.", vbExclamation
End If
On Error GoTo 0
' Delete rows.
Set dcrg = dws.Range(dcrgAddress)
Set ddrg = dws.Range(ddrgAddress)
dcrg.AutoFilter 1, "<>" & dKey
On Error Resume Next
Set drg = ddrg.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
dws.AutoFilterMode = False ' to not delete entire rows
If Not drg Is Nothing Then
drg.Delete xlShiftUp
Set drg = Nothing
End If
Set dws = Nothing
Next dKey
sws.Activate
Application.ScreenUpdating = True
MsgBox "Unique worksheets created.", vbInformation
End Sub

VBA to Consolidate data from folder to single sheet in Excel

I just found the below vba code from this forum and trying to include column headers of the excel files to be copied but no luck. please help.
Sub ConsolidateWorkbooks()
Dim FolderPath As String, Filename As String, sh As Worksheet, ShMaster As Worksheet
Dim wbSource As Workbook, lastER As Long, arr
'adding a new sheet on ThisWorkbook (after the last existing one)
Set ShMaster = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Worksheets(ThisWorkbook.Sheets.count))
Application.ScreenUpdating = False
FolderPath = "P:\FG\03_OtD_Enabling\Enabling\Teams\Enabling_RPA\Other Automations\Excel Merge Several Files\Data\"
Filename = Dir(FolderPath & "*.xls*")
Do While Filename <> ""
'set the workbook to be open:
Set wbSource = Workbooks.Open(Filename:=FolderPath & Filename, ReadOnly:=True)
For Each sh In ActiveWorkbook.Worksheets 'iterate between its sheets
lastER = ShMaster.Range("A" & rows.count).End(xlUp).row 'last empty row
'put the sheet range in an array:
arr = sh.Range(sh.UsedRange.cells(1, 1).Offset(1, 0), _
sh.cells(sh.UsedRange.rows.count - sh.UsedRange.row + 1, _
sh.UsedRange.Columns.count)).Value
'drop the array content at once:
ShMaster.Range("A" & lastER).Resize(UBound(arr), UBound(arr, 2)).Value = arr
Next sh
wbSource.Close 'close the workbook
Filename = Dir() 'find the next workbook in the folder
Loop
Application.ScreenUpdating = True
End Sub
Consolidate Workbooks
This will copy only the headers of each first worksheet of each workbook.
If you meant to copy the headers of each worksheet, it becomes much simpler i.e. surg, srCount and sIsFirstWorksheet become redundant:
For Each sws In swb.Worksheets
Set srg = sws.UsedRange
dCell.Resize(srg.Rows.Count, srg.Columns.Count).Value = srg.Value
Set dCell = dCell.Offset(srg.Rows.Count)
Next sws
If you want one or more empty rows between the data sets, you can easily implement a constant (e.g. Const Gap As Long = 1) and add it to the 'offset part':
Set dCell = dCell.Offset(srCount + Gap)
Option Explicit
Sub ConsolidateWorkbooks()
Const ProcTitle As String = "Consolidate Workbooks"
Const sFolderPath As String = "P:\FG\03_OtD_Enabling\Enabling\Teams\" _
& "Enabling_RPA\Other Automations\Excel Merge Several Files\Data\"
Const sFilePattern As String = "*.xls*"
' Source (Are there any files?)
Dim sFileName As String: sFileName = Dir(sFolderPath & sFilePattern)
If Len(sFileName) = 0 Then
MsgBox "No files to process.", vbCritical, ProcTitle
Exit Sub
End If
Application.ScreenUpdating = False
' Destination (Workbook - Worksheet - Range (First Cell))
Dim dwb As Workbook: Set dwb = ThisWorkbook
Dim dws As Worksheet ' note 'Worksheets vs Sheets':
Set dws = dwb.Worksheets.Add(After:=dwb.Sheets(dwb.Sheets.Count))
Dim dCell As Range
Set dCell = dws.Cells(dws.Rows.Count, 1).End(xlUp).Offset(1)
' Source (Variables)
Dim swb As Workbook
Dim sws As Worksheet
Dim surg As Range
Dim srg As Range
Dim srCount As Long
Dim sFilePath As String
Dim sIsFirstWorksheet As Boolean
Do While Len(sFileName) > 0
sFilePath = sFolderPath & sFileName
Set swb = Workbooks.Open(Filename:=sFilePath, ReadOnly:=True)
sIsFirstWorksheet = True
For Each sws In swb.Worksheets
Set surg = sws.UsedRange
If sIsFirstWorksheet Then ' copy headers
srCount = surg.Rows.Count
Set srg = surg
sIsFirstWorksheet = False
Else ' don't copy headers
srCount = surg.Rows.Count - 1
Set srg = surg.Resize(srCount).Offset(1)
End If
dCell.Resize(srCount, srg.Columns.Count).Value = srg.Value
Set dCell = dCell.Offset(srCount)
Next sws
swb.Close SaveChanges:=False
sFileName = Dir
Loop
'dwb.Save
Application.ScreenUpdating = True
MsgBox "Workbooks consolidated.", vbInformation, ProcTitle
End Sub

Resources