Copy values from multiple sheets to summary sheet - excel

In the image, there are some empty cells in column L,M, W:Z.
I am trying to loop through all sheets in the workbook.
Starting from Sheet1, filter out the empty "L" cells under the blue header in "A7",
copy the array of values (between A:Z or all cells with values in the row, ideally),
paste the copied array in the summary sheet,
Copy P2 for each sheet and paste the value as a separator between sheets.
Then continue a loop through the sheets.
Typically these workbooks have between 100-150 sheets.
These workbooks are generated for work, so I have adjusted the values accordingly.
South Park references everywhere is my style with VBA since nobody else sees them.
Issue: row numbers are dynamic, and I do not know how to offset from row "A7" after filtering without variation.
Sub Missing_L_Value_Summary()
Dim MyRange As Range
Dim MyCell As Range
Dim ws As Worksheet, myValue
Dim lCount As Long
Dim title As Long
Dim rng As Range
Application.ScreenUpdating = False
Application.EnableEvents = False
ActiveSheet.Name = "Sheet1"
'Workbook.Save.Name = Range("A2") & "James Cameron"
'Range("A2").Copy
Sheets.Add.Name = "Summary"
Sheets("Summary").Select
'Range("A1").PasteSpecial
ActiveCell.Offset(2, 1).Select
Sheets("Sheet1").Select
Range("A8").Copy
Sheets("Summary").Select
ActiveCell.PasteSpecial
Range("B3").EntireColumn.AutoFit
Sheets("Sheet1").Select
Range("$A$7:$Z$7").Copy
Sheets("Summary").Select
ActiveCell.Offset(1, 0).PasteSpecial
Sheets("Sheet1").Select
For Each ws In Sheets
Range("L7").Select
With ws.Cells(7, 12).CurrentRegion
.AutoFilter Field:=12, Criteria1:="="'
Application.ScreenUpdating = True
Application.EnableEvents = True
MsgBox("James Cameron doesn't do what James Cameron does for James Cameron. James Cameron does
End Sub
what James Cameron does for James Cameron!")

Get Filtered Rows
Option Explicit
Sub Missing_L_Value_Summary()
Const ProcName As String = "Missing_L_Value_Summary"
On Error GoTo ClearError
Dim IsSuccess As Boolean
Const sExceptionsList As String = "Summary" ' add more
Const sExceptionsDelimiter As String = ","
Const sBeforeSheetName As String = "Sheet1"
Const sfCellAddressCR As String = "L7"
Const sDateAddress As String = "P2"
Const sField As Long = 12
Const sCriteria As String = "="
Const dName As String = "Summary"
Const dfCellAddress As String = "A3"
Const dDateCol As String = "B"
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim dws As Worksheet
On Error Resume Next ' prevent error if it doesn't exist
Set dws = wb.Worksheets(dName)
On Error GoTo ClearError
If Not dws Is Nothing Then
Application.DisplayAlerts = False ' delete without confirmation
dws.Delete
Application.DisplayAlerts = True
End If
Set dws = wb.Worksheets.Add(Before:=wb.Worksheets(sBeforeSheetName))
dws.Name = dName
Dim dCell As Range: Set dCell = dws.Range(dfCellAddress)
Dim sExceptions() As String
sExceptions = Split(sExceptionsList, sExceptionsDelimiter)
Dim sws As Worksheet
Dim srg As Range
Dim svrg As Range
Dim drg As Range
Dim dData As Variant
Dim drCount As Long
Dim ErrNum As Long
For Each sws In wb.Worksheets
If IsError(Application.Match(sws.Name, sExceptions, 0)) Then
If sws.AutoFilterMode Then sws.AutoFilterMode = False
' Write date.
dCell.EntireRow.Columns(dDateCol).Value = sws.Range(sDateAddress)
Set dCell = dCell.Offset(1)
' Write data.
Set srg = sws.Range(sfCellAddressCR).CurrentRegion
On Error Resume Next
srg.AutoFilter sField, sCriteria
ErrNum = Err.Number
On Error GoTo ClearError
If ErrNum = 0 Then
On Error Resume Next
Set svrg = srg.SpecialCells(xlCellTypeVisible)
On Error GoTo ClearError
sws.AutoFilterMode = False
If Not svrg Is Nothing Then
dData = GetFilteredRows(svrg)
If Not IsEmpty(dData) Then
drCount = UBound(dData, 1)
Set drg = dCell.Resize(drCount, UBound(dData, 2))
drg.Value = dData
Set dCell = dCell.Offset(drCount)
Set svrg = Nothing
End If
End If
End If
End If
Next sws
IsSuccess = True
SafeExit:
If Application.EnableEvents = False Then
Application.EnableEvents = True
Application.ScreenUpdating = True
End If
If IsSuccess Then
MsgBox "James Cameron doesn't do what James Cameron does " _
& "for James Cameron. James Cameron does what James Cameron does " _
& "for James Cameron!", vbInformation
Else
MsgBox "Something went wrong.", vbCritical
End If
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume SafeExit
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a filtered range in a 2D one-based array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetFilteredRows( _
ByVal FilteredRange As Range) _
As Variant
Const ProcName As String = "GetFilteredRows"
On Error GoTo ClearError
Dim saCount, drCount, cCount
With FilteredRange
saCount = .Areas.Count
drCount = Intersect(.Offset(0), _
.Worksheet.Columns(.Cells(1).Column)).Cells.Count
cCount = .Areas(1).Columns.Count
End With
Dim dData As Variant: ReDim dData(1 To drCount, 1 To cCount)
Dim sarg As Range
Dim sData As Variant
Dim srCount As Long, sr As Long, dr As Long, c As Long
For Each sarg In FilteredRange.Areas
srCount = sarg.Rows.Count
If cCount + srCount > 2 Then
sData = sarg.Value
Else
ReDim sData(1 To 1, 1 To 1)
sData(1, 1) = sarg.Value
End If
For sr = 1 To srCount
dr = dr + 1
For c = 1 To cCount
dData(dr, c) = sData(sr, c)
Next c
Next sr
Next sarg
GetFilteredRows = dData
ProcExit:
Exit Function
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
End Function

i use only offset formula because if i remove lines or columns he never give error
ex: if im in cell B5 of sheet2 and want show same information from sheet1
=OFFSET(sheet1!$A$1;ROW(B5)-1;COLUMN(B5)-1)
Only cell fix are A1 sheet1

Related

Empty rows in file generated from macros in excel

I am trying to split a file with 120 records into files of at-most 50 records each. So expectation is it should genarate 2 files with 50 records and 1 file wit 20 but what I am getting is 3 files of 51 records with 1 empty file in the end for first 2 and 31 empty lines in 3rd file.
Sub SplitAndSaveFile()
Dim myRow As Long, myBook As Workbook, splitCount As Integer, thisWBName As String, splitCountStr As String, spaceRange As Range
lastRow = ThisWorkbook.Sheets("Data").Cells(rows.Count, 1).End(xlUp).Row
splitCount = 1
splitCountStr = CStr(splitCount)
thisWBName = Replace(ThisWorkbook.Name, ".xlsm", "") + "_Part"
For myRow = 4 To lastRow Step 50
Set myBook = Workbooks.Add
ThisWorkbook.Sheets("Data").rows(myRow & ":" & myRow + 49).EntireRow.Copy myBook.Sheets("Sheet1").Range("A1")
myBook.SaveAs (ThisWorkbook.Path + "\" + thisWBName + splitCountStr + ".txt"), FileFormat:=xlText
myBook.Close
splitCount = splitCount + 1
splitCountStr = CStr(splitCount)
Next myRow
MsgBox ("File(s) generated.")
End Sub
Export Data by Number of Rows
A Partial Quick Fix
Your code seemed to work fine on my testing data, so the only thing I could think of, considering your description of the issue, was that in column A there are formulas evaluating to an empty string at the bottom, which you don't want to include. To fix this, you could use the Find method:
Dim LastRow As Long: LastRow = ThisWorkbook.Worksheets("Data") _
.Columns("A").Find("*", , xlValues, , , xlPrevious)
Unfortunately, you also didn't consider the case when there will be fewer than 50 records to be copied to the last workbook. See how it is handled in the 'In-Depth' solution.
In Depth
This will export the records in a worksheet to new workbooks, saved as text, containing maximally 50 rows.
Option Explicit
Sub SplitAndSaveFile()
Const ProcName As String = "SplitAndSaveFile"
Dim dwbCount As Long ' Generated Workbooks Count
On Error GoTo ClearError
' Source
Const swsName As String = "Data"
Const sCol As String = "A"
Const sfRow As Long = 4
' Destination
Const dfCellAddress As String = "A1" ' needs to be 'A' since entire rows.
Const dMaxRows As Long = 50
Const dNameSuffix As String = "_Part"
' In the loop, this will be replaced by a number ('dwbCount').
Const dIdPlaceHolder As String = "?" ' the '?' is illegal for file names
' The following two lines are dependent on each other.
Const dFileExtension As String = ".txt"
Dim dFileFormat As XlFileFormat: dFileFormat = xlText
' Create a reference to the source first cell ('sfCell').
Dim swb As Workbook: Set swb = ThisWorkbook
Dim sws As Worksheet: Set sws = swb.Worksheets(swsName)
Dim sfCell As Range: Set sfCell = sws.Cells(sfRow, sCol)
' Calculate the number of records (rows) ('drCount').
' This will find the last non-blank cell i.e. cells containing
' formulas evaluating to an empty string are ignored.
' Make sure that the worksheet is not filtered and there are no hidden
' cells.
Dim slCell As Range
Set slCell = sfCell.Resize(sws.Rows.Count - sfRow + 1) _
.Find("*", , xlValues, , , xlPrevious)
If slCell Is Nothing Then Exit Sub ' no data
Dim slRow As Long: slRow = slCell.Row
' This is the preferred way, but besides a few pros, it behaves like 'End'
' i.e. it will find the last non-empty cell. A cell is not empty
' if it contains a formula evaluating to an empty string ('""'):
' it is blank.
'Dim slCell As Range
'Set slCell = sfCell.Resize(sws.Rows.Count - sfRow + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
'If slCell Is Nothing Then Exit Sub ' no data
'Dim slRow As Long: slRow = slCell.Row
' The classic last row using 'End' will find the last non-empty cell.
'Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, sCol).End(xlUp).Row
Dim drCount As Long: drCount = slRow - sfRow + 1
If drCount < 1 Then Exit Sub ' no data (highly unlikely but...)
' Determine the generic file path (dwbGenericFilePath)
Dim swbBaseName As String: swbBaseName = swb.Name
Dim DotPosition As String: DotPosition = InStrRev(swb.Name, ".")
If DotPosition > 0 Then swbBaseName = Left(swbBaseName, DotPosition - 1)
Dim dwbExtension As String: dwbExtension = dFileExtension
If Left(dwbExtension, 1) <> "." Then dwbExtension = "." & dwbExtension
Dim dwbGenericFilePath As String
dwbGenericFilePath = swb.Path & Application.PathSeparator & swbBaseName _
& dNameSuffix & dIdPlaceHolder & dwbExtension
Application.ScreenUpdating = False
' Additional variables used in the loop.
Dim srg As Range
Dim dwb As Workbook
Dim dws As Worksheet
Dim dfCell As Range
Dim dFilePath As String
Do Until drCount = 0
' Create a reference to the current source range.
If drCount > dMaxRows Then ' all workbooks but the last
Set srg = sfCell.Resize(dMaxRows).EntireRow
Set sfCell = sfCell.Offset(dMaxRows)
drCount = drCount - dMaxRows
Else ' the last workbook
Set srg = sfCell.Resize(drCount).EntireRow
drCount = 0
End If
' Copy the current source range to the current destination range.
dwbCount = dwbCount + 1 ' count the number of generated workbooks
Set dwb = Workbooks.Add(xlWBATWorksheet) ' one worksheet only
Set dws = dwb.Worksheets(1)
Set dfCell = dws.Range(dfCellAddress)
srg.Copy dfCell
' Save and close the current destination workbook.
dFilePath = Replace(dwbGenericFilePath, dIdPlaceHolder, CStr(dwbCount))
Application.DisplayAlerts = False ' overwrite without confirmation
dwb.SaveAs dFilePath, dFileFormat
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Loop
ProcExit:
Application.ScreenUpdating = True
Select Case dwbCount
Case 0
MsgBox "No files generated.", vbCritical, ProcName
Case 1
MsgBox "One file generated.", vbInformation, ProcName
Case Else
MsgBox dwbCount & " files generated.", vbInformation, ProcName
End Select
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "' Run-time error '" _
& Err.Number & "':" & vbLf & " " & Err.Description
Resume ProcExit
End Sub

How can I merge two excel vba code into one which captures dynamically changing values

I have the below two excel vba codes which are almost alike but I want to merge them into one:
Code 1:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ClearError
Const sfCellAddress As String = "A2" ' source
Const lCol As String = "B" ' lookup
Const dCol As String = "C" ' destination
Const Criteria As String = "CENTER"
Dim sfCell As Range: Set sfCell = Range(sfCellAddress)
Dim srg As Range: Set srg = sfCell.Resize(Rows.Count - sfCell.Row + 1)
Dim sirg As Range: Set sirg = Intersect(srg, Target)
If sirg Is Nothing Then Exit Sub
' Relevant Ranges (lcol, dcol)
Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
Dim cLen As Long: cLen = Len(Criteria)
Dim lString As String
Dim dString As String
Dim r As Long
Application.EnableEvents = False
For r = 1 To lrg.Cells.Count
lString = CStr(lrg.Cells(r).Value)
If Len(lString) > 0 Then
dString = CStr(drg.Cells(r).Value)
If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
If Len(dString) = 0 Then
dString = lString
Else
dString = dString & "," & lString
End If
drg.Cells(r).Value = dString
End If
End If
Next r
SafeExit:
If Not Application.EnableEvents Then
Application.EnableEvents = True
End If
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit
End Sub
Code 2
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ClearError
Const sfCellAddress As String = "A2" ' source
Const lCol As String = "D" ' lookup
Const dCol As String = "E" ' destination
Const Criteria As String = "SURFACE"
Dim sfCell As Range: Set sfCell = Range(sfCellAddress)
Dim srg As Range: Set srg = sfCell.Resize(Rows.Count - sfCell.Row + 1)
Dim sirg As Range: Set sirg = Intersect(srg, Target)
If sirg Is Nothing Then Exit Sub
' Relevant Ranges (lcol, dcol)
Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
Dim cLen As Long: cLen = Len(Criteria)
Dim lString As String
Dim dString As String
Dim r As Long
Application.EnableEvents = False
For r = 1 To lrg.Cells.Count
lString = CStr(lrg.Cells(r).Value)
If Len(lString) > 0 Then
dString = CStr(drg.Cells(r).Value)
If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
If Len(dString) = 0 Then
dString = lString
Else
dString = dString & "," & lString
End If
drg.Cells(r).Value = dString
End If
End If
Next r
SafeExit:
If Not Application.EnableEvents Then
Application.EnableEvents = True
End If
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit
End Sub
From what I can work out, you want to take the reasonably generic code and make it reusable.
Try this.
Create a new module in the VBA editor and paste this code. It's a slight change on the code you had on each worksheet. I've added the Target parameter and referred directly to the worksheet that was changed ...
Public Sub OnSheetChange(ByVal Target As Range, ByVal sfCellAddress As String, ByVal lCol As String, _
ByVal dCol As String, ByVal Criteria As String)
On Error GoTo ClearError
Dim objSheet As Worksheet
Set objSheet = Target.Worksheet
Dim sfCell As Range: Set sfCell = objSheet.Range(sfCellAddress)
Dim srg As Range: Set srg = sfCell.Resize(objSheet.Rows.Count - sfCell.Row + 1)
Dim sirg As Range: Set sirg = Intersect(srg, Target)
If Not sirg Is Nothing Then
' Relevant Ranges (lcol, dcol)
Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, objSheet.Columns(lCol))
Dim drg As Range: Set drg = Intersect(sirg.EntireRow, objSheet.Columns(dCol))
Dim cLen As Long: cLen = Len(Criteria)
Dim lString As String
Dim dString As String
Dim r As Long
Application.EnableEvents = False
For r = 1 To lrg.Cells.Count
lString = CStr(lrg.Cells(r).Value)
If Len(lString) > 0 Then
dString = CStr(drg.Cells(r).Value)
If StrComp(Right(dString, cLen), Criteria, vbTextCompare) <> 0 Then
If Len(dString) = 0 Then
dString = lString
Else
dString = dString & "," & lString
End If
drg.Cells(r).Value = dString
End If
End If
Next r
End If
SafeExit:
If Not Application.EnableEvents Then
Application.EnableEvents = True
End If
Exit Sub
ClearError:
Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
Resume SafeExit
End Sub
... now from the Worksheet_OnChange event method, do something like this ...
Private Sub Worksheet_Change(ByVal Target As Range)
OnSheetChange Target, "A2", "B", "C", "CENTER"
OnSheetChange Target, "A2", "D", "E", "SURFACE"
End Sub
... that will make your code reusable. Naturally, you will need to make sure it works perfectly for you but that's the general idea.
Merging Similar Worksheet_Change Codes
Description
For each cell manually changed (enter, copy/paste or VBA write) in column A (cell A1 excluded)...
... in the same row of each column in the lookup columns list (lColsList - B) ...
... it will try to find the value (B) in the associated criteria list (CriteriaList - CENTER;BOTTOM).
If the value (B) is found:
If the value (B / CENTER;BOTTOM) is already in the cell of the associated destination column (dColsList - C) it will do nothing. The cell is 'sealed'.
If not, the value (B) will be appended to the cell (C) 'sealing' the cell due to the previous condition.
If the value (B) is not found:
If there already is a value from the criteria list (CENTER;BOTTOM) it will do nothing since the cell is 'sealed'.
If not:
If the value (B) is already in the destination cell (C), it will do nothing.
If not, the value (B) will be appended to the cell (C).
The Code
Adjust the values in the constants section.
You may want to remove ;BOTTOM since its purpose is just to illustrate that you can have more criteria per column to 'seal' ('freeze') a cell.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Calls: Worksheet_Change
' DelimitOnChange
' DelimitOnChangeWrite
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
DelimitOnChange Target
End Sub
Private Sub DelimitOnChange( _
ByVal Target As Range)
Const ProcName As String = "DelimitOnChange"
On Error GoTo ClearError
Const sfCellAddress As String = "A2" ' source
Const lColsList As String = "B,D" ' lookup
Const dColsList As String = "C,E" ' destination
Const CriteriaList As String = "CENTER;BOTTOM,SURFACE"
Const ListDelimiter As String = "," ' 3 lists (see right above)
Const CriteriaDelimiter As String = ";" ' multiple criteria per column
Const ValuesDelimiter As String = "," ' values in lookup column
Dim srg As Range
With Target.Worksheet
Dim sfCell As Range: Set sfCell = .Range(sfCellAddress)
Set srg = sfCell.Resize(.Rows.Count - sfCell.Row + 1)
End With
Dim sirg As Range: Set sirg = Intersect(srg, Target)
If sirg Is Nothing Then Exit Sub
Dim lCols() As String: lCols = Split(lColsList, ListDelimiter)
Dim dCols() As String: dCols = Split(dColsList, ListDelimiter)
Dim Criteria() As String: Criteria = Split(CriteriaList, ListDelimiter)
Application.EnableEvents = False
Dim n As Long
For n = 0 To UBound(lCols)
DelimitOnChangeWrite sirg, lCols(n), dCols(n), Criteria(n), _
CriteriaDelimiter, ValuesDelimiter
Next n
SafeExit:
If Not Application.EnableEvents Then
Application.EnableEvents = True
End If
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume SafeExit
End Sub
Private Sub DelimitOnChangeWrite( _
ByVal sirg As Range, _
ByVal lCol As String, _
ByVal dCol As String, _
ByVal CriteriaList As String, _
Optional ByVal CriteriaDelimiter As String = ";", _
Optional ByVal ValuesDelimiter As String = ",")
Const ProcName As String = "DelimitOnChangeWrite"
On Error GoTo ClearError
Dim Criteria() As String: Criteria = Split(CriteriaList, CriteriaDelimiter)
Dim cUpper As Long: cUpper = UBound(Criteria)
Dim lrg As Range: Set lrg = Intersect(sirg.EntireRow, Columns(lCol))
Dim drg As Range: Set drg = Intersect(sirg.EntireRow, Columns(dCol))
Dim lString As String
Dim dString As String
Dim c As Long
Dim cIndex As Variant
Dim r As Long
For r = 1 To lrg.Cells.Count
lString = CStr(lrg.Cells(r).Value)
If Len(lString) > 0 Then
dString = CStr(drg.Cells(r).Value)
If Len(dString) = 0 Then
dString = lString
Else
For c = 0 To cUpper
If StrComp(Right(dString, Len(Criteria(c))), _
Criteria(c), vbTextCompare) = 0 Then Exit For
Next c
If c > cUpper Then
If InStr(1, dString, lString, vbTextCompare) = 0 Then
dString = dString & ValuesDelimiter & lString
End If
End If
End If
drg.Cells(r).Value = dString
End If
Next r
ProcExit:
Exit Sub
ClearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
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

Append data after last row

My macro is to append the values from three sheets in a weekly report workbook to the values in equivalent sheets in an accumulating workbook.
However, I cannot define the ranges in the ThisWorkbook.Sheets correctly - Only the cell A2 values from the wb.Sheets are appended.
Could someone please help me define the range correctly? Many thanks!
Sub Import_SheetData_ThisWorkbook()
Dim lRow As Long, lRow1 As Long, lRow2 As Long, lRow3 As Long
Dim Path As String, WeeklyCollation As String
Dim wkNum As Integer
Dim wb As Workbook
wkNum = Application.InputBox("Enter week number")
Path = "C:\xyz\"
WeeklyCollation = Path & "Activities 2021 w" & wkNum & ".xlsx"
lRow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row + 1
Set wb = Workbooks.Open(WeeklyCollation)
lRow1 = wb.Sheets("Customer visits").Cells(Rows.Count, 1).End(xlUp).Row
lRow2 = wb.Sheets("Orders").Cells(Rows.Count, 1).End(xlUp).Row
lRow3 = wb.Sheets("Visits").Cells(Rows.Count, 1).End(xlUp).Row
'Replace with copy and paste
'Can't define range in ThisWorkbook
ThisWorkbook.Sheets("Customer visits").Range("A" & lRow + 1).Value = wb.Sheets("Customer visits").Range("A2:H" & lRow1).Value
ThisWorkbook.Sheets("Orders").Range("A" & lRow + 1).Value = wb.Sheets("Orders").Range("A2:I" & lRow2).Value
ThisWorkbook.Sheets("Visits").Range("A" & lRow + 1).Value = wb.Sheets("Visits").Range("A2:F" & lRow3).Value
wb.Close SaveChanges:=False
MsgBox ("Data added")
End Sub
Backup Data
Adjust the values in the constants section.
Option Explicit
Sub ImportSheetData()
' Constants
' Source
Const sPath As String = "C:\xyz\"
Const swsNamesList As String = "Customer visits,Orders,Visits"
Const slrCol As String = "A"
Const sfRow As Long = 2
' Destination
Const dwsNamesList As String = "Customer visits,Orders,Visits"
Const dlrCol As String = "A"
' Both
Const Cols As String = "A:H"
' Create the references to the workbooks.
Dim wkNum As Variant: wkNum = Application.InputBox( _
"Enter week number", "Import Sheet Data", , , , , , 1)
If wkNum = False Then
MsgBox "You canceled.", vbExclamation
Exit Sub
End If
Dim sWeeklyCollation As String
sWeeklyCollation = sPath & "Activities 2021 w" & wkNum & ".xlsx"
Dim swb As Workbook
On Error Resume Next
Set swb = Workbooks.Open(sWeeklyCollation)
On Error GoTo 0
If swb Is Nothing Then
MsgBox "Could not find the file '" & sWeeklyCollation & "'.", vbCritical
Exit Sub
End If
Dim dwb As Workbook: Set dwb = ThisWorkbook ' workbook containing this code
' Copy the data.
Dim swsNames() As String: swsNames = Split(swsNamesList, ",")
Dim dwsNames() As String: dwsNames = Split(dwsNamesList, ",")
Dim sws As Worksheet
Dim srg As Range
Dim slRow As Long
Dim dws As Worksheet
Dim drg As Range
Dim dCell As Range
Dim n As Long
Dim rCount As Long
Dim wsCount As Long ' Counts the number of worksheets processed
For n = 0 To UBound(swsNames)
On Error Resume Next
Set sws = swb.Worksheets(swsNames(n))
On Error GoTo 0
If Not sws Is Nothing Then ' source worksheet exists
On Error Resume Next
Set dws = dwb.Worksheets(dwsNames(n))
On Error GoTo 0
If Not dws Is Nothing Then ' destination worksheet exists
slRow = sws.Cells(sws.Rows.Count, slrCol).End(xlUp).Row
rCount = slRow - sfRow + 1
If rCount > 0 Then ' found data in source worksheet
Set srg = sws.Columns(Cols).Resize(rCount).Offset(sfRow - 1)
Set dCell = dws.Cells(dws.Rows.Count, dlrCol) _
.End(xlUp).Offset(1)
Set drg = dCell.Resize(rCount).EntireRow.Columns(Cols)
drg.Value = srg.Value
wsCount = wsCount + 1
' Else ' no data in source worksheet
End If
'Else ' destination worksheet doesn't exist
End If
'Else ' source worksheet doesn't exist
End If
Next n
' Finishing Touches
swb.Close SaveChanges:=False
'dwb.Save
' Or:
'dwb.Close SaveChanges:=True
MsgBox "Data from " & wsCount & " worksheets added.", vbInformation
End Sub

Merge Two Columns of Data into a Single Variable

I have a spreadsheet with two columns of data, both columns have a header. I would like to establish a variable for each row of data I can then use to generate new worksheet names and insert into formulas. My variable would be a one to one ratio with the data, meaning A2-B2, A3-B3, etc. I have tried the following code:
'''Sub CreateSheet2()
Dim rngBP As Range
Dim rngCon As Range
Dim cellBP As Range
Dim cellCon As Range
On Error GoTo Errorhandling
Set rngBP = Application.InputBox(prompt:="Bid Package Select Cell Range:", Title:="Create Sheets", Default:=Selection.Address, Type:=8)
Set rngCon = Application.InputBox(prompt:="Contractor Select Cell Range:", Title:="Create Sheets", Default:=Selection.Address, Type:=8)
For Each cellBP In rngBP
If cellBP <> "" And cellCon <> "" Then
Sheets.Add(after:=ActiveWorkbook.Worksheets(ActiveWorkbook.Worksheets.Count)).Name = cellBP & "-" & cellCon
End If
Next cellBP
Errorhandling:
MsgBox prompt:="Error Detected" & vbNewLine & "Error" & Err.Number & ": " & Err.Description
End Sub'''
However, this code generates a variable with all of the possible combinations (A2-B2, A2-B3, A3-B3, etc.). Ideally, this code would also skip empty cells and not create a variable for that entire row. Here is a screenshot of my sample dataset.Sample Dataset. Thank you for the assistance.
Add Worksheets with Names Created from Two Columns
I see the double Application.InputBoxes as a disaster waiting to happen so I abandoned the idea.
The code will search for the specified headers in the first row and their columns will define the column ranges (from the 2nd to the last row).
Copy the code into a standard module, e.g. Module1.
Adjust the four constants.
You only run the first procedure which will call the second when needed.
The third procedure is showing an example of proper error handling. Study it closely.
The Code
Option Explicit
Sub CreateSheet2()
'On Error GoTo ErrorHandling
Const wsName As String = "Sheet1"
Const bTitle As String = "Bid Package"
Const cTitle As String = "Contractor"
Const FirstRow As Long = 2
Dim wb As Workbook
Set wb = ThisWorkbook ' The workbook containing this code.
Dim ws As Worksheet
Set ws = wb.Worksheets(wsName)
Dim bCol As Variant
bCol = Application.Match(bTitle, ws.Rows(1), 0)
Dim cCol As Variant
cCol = Application.Match(cTitle, ws.Rows(1), 0)
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, bCol).End(xlUp).Row
Dim ColumnOffset As Long
ColumnOffset = cCol - bCol
Dim SheetNames As Variant
SheetNames = getSheetNames(wb)
Dim rng As Range
Set rng = ws.Cells(FirstRow, bCol).Resize(LastRow - FirstRow + 1)
Dim cel As Range
Dim SheetName As String
For Each cel In rng.Cells
If cel.Value <> "" And cel.Offset(, ColumnOffset).Value <> "" Then
SheetName = cel.Value & "-" & cel.Offset(, ColumnOffset).Value
If IsError(Application.Match(SheetName, SheetNames, 0)) Then
On Error Resume Next
wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count)).Name _
= SheetName
If Err Then ' might happen if there are duplicates in columns.
Application.DisplayAlerts = False
ActiveSheet.Delete
Application.DisplayAlerts = True
End If
On Error GoTo ErrorHandling
End If
End If
Next cel
ProcExit:
Exit Sub
ErrorHandling:
MsgBox Prompt:="Error Detected" & vbNewLine & "Error '" & Err.Number _
& "': " & Err.Description, _
Buttons:=vbCritical, _
Title:="Fail"
Resume ProcExit
End Sub
Function getSheetNames(Book As Workbook) _
As Variant
If Book Is Nothing Then
GoTo ProcExit
End If
Dim Data As Variant
ReDim Data(1 To Book.Sheets.Count)
Dim sh As Object
Dim n As Long
For Each sh In Book.Sheets
n = n + 1
Data(n) = sh.Name
Next sh
getSheetNames = Data
ProcExit:
End Function
Proper Error Handling
Sub ProperErrorHandling()
On Error GoTo ErrorHandling
' The code
ProcExit:
Exit Sub ' You don't want to show the message if no error and
' you must not 'Resume' with no error!
ErrorHandling:
MsgBox Prompt:="Error Detected" & vbNewLine & "Error '" & Err.Number _
& "': " & Err.Description, _
Buttons:=vbCritical, _
Title:="Fail"
' Sets the error number to 0, but still keeps the error handler active.
' Therefore be aware that if you put code between 'ProcExit' and 'Exit Sub'
' and an error occurs, it will result in an endless loop.
Resume ProcExit ' 'Resume', not 'GoTo'!
End Sub
Please try this code.
Sub CreateSheet2()
Const BidPack As String = "A" ' specify a column
Const Contractor As String = "B" ' change to suit (to the right of BidPack)
Const FirstDataRow As Long = 2 ' change to suit
Dim Wb As Workbook
Dim Ws As Worksheet
Dim BidRng As Range
Dim ConRng As Range
Dim Tmp As Variant ' misc use
Dim WsName As String
Dim R As Long ' loop counter: rows
Set Wb = ActiveWorkbook ' change to suit
WsName = "Sheet1" ' change to suit
Application.ScreenUpdating = False
Tmp = Columns(Contractor).Column
With Wb.Worksheets(WsName)
Set ConRng = .Range(.Cells(FirstDataRow, Tmp), _
.Cells(.Rows.Count, Tmp).End(xlUp))
' ConRng and BidRng are of identical size,
' not exceeding the number of rows available in ConRng.
Set BidRng = ConRng.Offset(, Columns(BidPack).Column - Tmp)
For R = 1 To BidRng.Cells.Count
If (Not IsEmpty(BidRng.Cells(R))) And (Not IsEmpty(ConRng.Cells(R))) Then
WsName = Format(BidRng.Cells(R).Value, "00-") & ConRng.Cells(R).Value
On Error Resume Next
Set Tmp = Wb.Sheets(WsName)
If Err Then
Wb.Sheets.Add(After:=Wb.Sheets(Wb.Sheets.Count)).Name = WsName
Else
MsgBox "A worksheet by the name of """ & WsName & _
""" already exists.", vbInformation, _
"Duplicate instruction"
End If
End If
Next R
End With
Application.ScreenUpdating = False
End Sub
I think it's best
loop through rngBP range not empty values, only
using a Dictionary object to ensure you're not duplicating sheet names
Option Explicit
Sub CreateSheets()
Dim rngBP As Range
Dim cellBP As Range
On Error GoTo Errorhandling
Set rngBP = Application.InputBox(prompt:="Bid Package Select Cell Range:", Title:="Create Sheets", Default:=Selection.Address, Type:=8)
Dim shNamesDict As Object
Set shNamesDict = CreateObject("Scripting.Dictionary")
With ActiveWorkbook
Dim shName As String
For Each cellBP In rngBP.SpecialCells(xlCellTypeConstants)
If Not IsEmpty(cellBP.Offset(, 1).Value2) Then
shName = cellBP.Value2 & "-" & cellBP.Offset(, 1).Value2
If Not shNamesDict.exists(shName) Then
shNamesDict.Add shName, 0
.Sheets.Add(after:=.Worksheets(.Worksheets.Count)).Name = shName
End If
End If
Next
End With
Errorhandling:
If Err.Number <> 0 Then MsgBox prompt:="Error Detected" & vbNewLine & "Error" & Err.Number & ": " & Err.Description
End Sub

Resources