VBA Function to Return LOOKUP Values Based on Column Range - excel

I am relatively new to VBA and would need help from the community on the below logic.
I have the following table
My Actual Data Table is as follows
My Expected Output is as follows:
I tried using index value to the cat codes and tried but I am stuck for logic here and not able to proceed. Thanks for your help.
Note: The Actual data need not contain the Catcode, for example value belonging to CatCode A will not always contain A in the value. I would to categorize all the values between two catcodes to the cat code that follows it.

Lookup Based on Column Range
Adjust the values in the constants section (e.g. The sheet names can be all the same, the first rows or columns can be different etc.).
New Version
Option Explicit
Sub LookupBasedOnColumnRange()
Const Head1 As String = "CatCode" ' 1st Column Header
Const Head2 As String = "Values" ' 2nd Column Header
Const cSheet As String = "Sheet1" ' CatCode Sheet Name
Const cFR As Long = 2 ' CatCode First Row Number (no header)
Const cCol As Variant = 1 ' CatCode Column (e.g. 1 or "A")
Const aSheet As String = "Sheet2" ' Actual Sheet Name
Const aFR As Long = 2 ' Actual First Row Number (no header)
Const aCol As Variant = 1 ' Actual Column (e.g. 1 or "A")
Const rSheet As String = "Sheet3" ' Result Sheet Name
Const rCel As String = "A1" ' Result First Cell Range Address
Dim rng As Range ' CatCode Non-Empty 1-Column Range,
' Actual Non-Empty 1-Column Range,
' Result 2-Column Range
Dim CatCode As Variant ' CatCode Array
Dim Actual As Variant ' Actual Array
Dim Result As Variant ' Result Array
Dim i As Long ' CatCode Array Elements Counter
Dim j As Long ' Actual Array Elements Counter,
' Result Array 1st Dimension (Rows) Elements Counter
' Change to "As Long" if only numbers
' or to "As Variant" if there are numbers and strings.
Dim CurC As String ' Current CatCode
Dim CurA As String ' Current Actual
' Write ranges to arrays.
With ThisWorkbook.Worksheets(cSheet)
Set rng = .Columns(cCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
CatCode = .Range(.Cells(cFR, cCol), rng)
End With
With ThisWorkbook.Worksheets(aSheet)
Set rng = .Columns(aCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
Actual = .Range(.Cells(aFR, aCol), rng)
End With
Set rng = Nothing
' Resize Result Array (Same first dimension (rows) as Actual Array).
ReDim Result(1 To UBound(Actual) + 1, 1 To 2) ' '+1' for headers
' Write headers to Result Array.
Result(1, 1) = Head1
Result(1, 2) = Head2
' Calculate and write data to Result Array.
j = 1
On Error GoTo ErrorHandler
For i = 1 To UBound(CatCode)
CurC = CatCode(i, 1)
Do
' If CatCode is missing, Run-time error '9'.
CurA = Actual(j, 1)
Result(j + 1, 1) = CurC
Result(j + 1, 2) = CurA
j = j + 1
Loop Until CurA = CurC Or j = UBound(Result) + 1
' "j = UBound(Result) + 1" prevents infinite loop
' if CatCode missing.
Next i
On Error GoTo 0
' Erase arrays not needed anymore.
Erase CatCode
Erase Actual
With ThisWorkbook.Worksheets(rSheet)
' Clear contents of columns of Result Range.
.Range(rCel).Resize(.Rows.Count - Range(rCel).Row + 1, 2).ClearContents
' Define Result Range.
Set rng = .Range(rCel).Resize(UBound(Result), UBound(Result, 2))
End With
' Copy Result Array to Result Range.
rng = Result
' Inform user.
MsgBox "Transferred Result(" & UBound(Result) & "x" & UBound(Result, 2) _
& ").", vbInformation, "Custom Message"
GoTo exitProcedure
ErrorHandler:
If Err.Number = 9 Then
MsgBox "CatCode '" & CurC & "' missing.", vbCritical, "Custom Message"
Err.Clear: GoTo exitProcedure
End If
If Err.Number > 0 Then
MsgBox "An unexpected error occurred. Error '" _
& Err.Number & "': " & Err.Description, vbCritical, "Custom Message"
Err.Clear: GoTo exitProcedure
End If
exitProcedure:
End Sub
Old Version Improved
Option Explicit
Sub LookupBasedOnColumnRangeFirst()
Const Head1 As String = "CatCode" ' 1st Column Header
Const Head2 As String = "Values" ' 2nd Column Header
Const cSheet As String = "Sheet1" ' CatCode Sheet Name
Const cFR As Long = 2 ' CatCode First Row Number (no header)
Const cCol As Variant = 1 ' CatCode Column (e.g. 1 or "A")
Const aSheet As String = "Sheet2" ' Actual Sheet Name
Const aFR As Long = 2 ' Actual First Row Number (no header)
Const aCol As Variant = 1 ' Actual Column (e.g. 1 or "A")
Const rSheet As String = "Sheet3" ' Result Sheet Name
Const rCel As String = "A1" ' Result First Cell Range Address
Dim rng As Range ' CatCode Non-Empty 1-Column Range,
' Actual Non-Empty 1-Column Range,
' Result 2-Column Range
Dim CatCode As Variant ' CatCode Array
Dim Actual As Variant ' Actual Array
Dim Result As Variant ' Result Array
Dim i As Long ' CatCode Array Elements Counter
Dim j As Long ' Actual Array Elements Counter
Dim k As Long ' Result Array 1st Dimension (Rows) Elements Counter
' Write ranges to arrays.
With ThisWorkbook.Worksheets(cSheet)
Set rng = .Columns(cCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
CatCode = .Range(.Cells(cFR, cCol), rng)
End With
With ThisWorkbook.Worksheets(aSheet)
Set rng = .Columns(aCol).Find(What:="*", LookIn:=xlFormulas, _
SearchDirection:=xlPrevious)
Actual = .Range(.Cells(aFR, aCol), rng)
End With
Set rng = Nothing
' The following line assumes that all 'data is valid'. If not then
' Result Array will have empty elements at the end (probably no harm done,
' but definately 'not correct'.
' Resize Result Array (Same first dimension (rows) as Actual Array).
ReDim Result(1 To UBound(Actual) + 1, 1 To 2) ' '+1' for headers
' Write headers to Result Array.
Result(1, 1) = Head1
Result(1, 2) = Head2
' Calculate and write data to Result Array.
k = 2
For i = 1 To UBound(CatCode)
For j = 1 To UBound(Actual)
If Actual(j, 1) Like CatCode(i, 1) & "*" Then
Result(k, 1) = CatCode(i, 1)
Result(k, 2) = Actual(j, 1)
k = k + 1
End If
Next j
Next i
' Note: The previous For Next Loop always loops through all elements
' of Actual Array allowing it to be unsorted.
' Erase arrays not needed anymore.
Erase CatCode
Erase Actual
With ThisWorkbook.Worksheets(rSheet)
' Clear contents of columns of Result Range.
.Range(rCel).Resize(.Rows.Count - Range(rCel).Row + 1, 2).ClearContents
' Define Result Range.
Set rng = .Range(rCel).Resize(UBound(Result), UBound(Result, 2))
End With
' Copy Result Array to Result Range.
rng = Result
' Inform user.
MsgBox "Transferred Result(" & UBound(Result) & "x" & UBound(Result, 2) _
& ").", vbInformation, "Custom Message"
End Sub

Related

How to use VBA to extract the data after the right arrow?

Hello everyone,
I'm doing a challenging task assigned by my supervisor but I'm not sure how to solve this problem. The task that I want to do is to extract the values after the right arrow in column B (Input) and paste it in column C (Output). If there is only one value in the cell (e.g. B4,B8,B9,...), then the output in column C will just be the value. If the cell in column B is empty, then just leave the output column as blank. So the final output should be exactly the same as column C. May I know is there any way to achieve it using VBA scripting? I know that this can be done without using VBA, but I'm requested to use VBA to solve this problem. Any help will be greatly appreciated!
Try the next function, please:
Function extractValLngArrSep(str As String) As Double
Dim x: x = Split(str, " ")
'For the case of empty cell - it will return 0:
If str = "" Then extractValLngArrSep = 0: Exit Function
If UBound(x) > 0 Then
extractValLngArrSep = x(2)
Else
extractValLngArrSep = x(0)
End If
End Function
It can be tested with the next testing Sub. It can be easily applied to all column, just iterating through its cells. Now, please activate one of such a cell with separated numbers and run the code. Then one cell having a simple number:
Sub testExtractVal()
ActiveCell.Offset(0, 1).Value = extractValLngArrSep(ActiveCell.Value)
End Sub
It should be also adapted to catch the case of a single string instead of a number, but this is something out of the question and not very difficult to be handled (I think...).
To process your example range, please use the next code:
Sub testExtractValBis()
Dim sh As Worksheet, lastRow As Long, C As Range
Set sh = ActiveSheet ' please, use here your necessary sheet
lastRow = sh.Range("B" & sh.Rows.count).End(xlUp).row
For Each C In sh.Range("B3:B" & lastRow)
If C.value <> "" Then
C.Offset(0, 1).Value = extractValLngArrSep(C.Value)
End Iff
Next
End Sub
Extract After Character
Adjust the values in the constants section.
The Code
Option Explicit
Sub extractAfterChar()
Const wsName As String = "Sheet1"
Const srcFirst As String = "B3"
Const dstfirst As String = "C3"
Dim Delimiter As String: Delimiter = ChrW(10141)
' Define workbook.
Dim wb As Workbook: Set wb = ThisWorkbook ' Workbook containing this code.
' Declare variables
Dim rg As Range ' Source Range
Dim wrCount ' Worksheet Rows Count
' Define Source Range.
With wb.Worksheets(wsName).Range(srcFirst)
wrCount = .Worksheet.Rows.Count
Set rg = .Resize(wrCount - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious) ' Define Last Cell Range.
If rg Is Nothing Then Exit Sub ' Validate Last Cell Range.
Set rg = .Resize(rg.Row - .Row + 1) ' Define Source Range.
End With
' Write Source Range's Rows Count to a variable.
Dim rCount As Long: rCount = rg.Rows.Count
' Write values from Source Range to Data Array.
Dim Data As Variant
If rCount > 1 Then
Data = rg.Value ' multiple rows (cells)
Else
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value ' one row (cell) only
End If
' Declare additional variables.
Dim Key As Variant ' Current Value (in Data Array)
Dim i As Long ' Data Array Row Counter
' Replace values in Data Array with results.
For i = 1 To rCount
Key = Data(i, 1)
If IsError(Key) Then
Data(i, 1) = Empty
Else
If Len(Key) = 0 Then ' empty or blank ("")
Data(i, 1) = Empty
Else
Key = Val(rightString(Key, Delimiter))
If Key = 0 Then
Data(i, 1) = Empty
Else
Data(i, 1) = Key
End If
End If
End If
Next
' Write values from Data Array to Destination Range.
With wb.Worksheets(wsName).Range(dstfirst)
.Resize(wrCount - .Row + 1).ClearContents ' from first to bottom-most
.Resize(rCount).Value = Data
End With
End Sub
Function rightString( _
ByVal SearchString As String, _
Optional ByVal Delimiter As String = ",", _
Optional ByVal doTrim As Boolean = True) _
As String
Dim slen As Long: slen = Len(SearchString)
If slen = 0 Then Exit Function
Dim sPos As Long: sPos = InStr(1, SearchString, Delimiter)
If sPos > 0 Then
rightString = Right(SearchString, slen - sPos)
Else
rightString = SearchString
End If
If doTrim Then
rightString = Trim(rightString)
End If
End Function

Excel VBA: What is the best way to sum a column in a dataset with variable amounts of lines?

I need to sum two columns (B and C) in a dataset. The number of rows with data will vary between 1 and 17. I need to add the sums two rows beneath the last row of data (end result example in image 1).
My code worked beautifully for one dataset, but I am getting an error
Run-time error'6': Overflow
for a different dataset. What am I doing wrong?
'Units total
Windows("Final_Files.xlsb").Activate
Sheets("Revenue Summary").Select
lastrow = Worksheets("Revenue Summary").Cells(Rows.Count, 2).End(xlUp).Row
Dim a As Integer
a = 10000
For i = lastrow To 2 Step by - 1
a = a + Worksheets("Revenue Summary").Cells(i, 2).Value
Next
Worksheets("Revenue Summary").Cells(lastrow + 2, 2).Value = a
Correct End Result
You can try below sub-
Sub SumBC()
Dim sh As Worksheet
Dim lRowB As Long, lRowC As Long
Dim bSum As Double, cSum As Double
Windows("Final_Files.xlsb").Activate
Set sh = Worksheets("Revenue Summary")
lRowB = sh.Cells(sh.Rows.Count, 2).End(xlUp).Row
lRowC = sh.Cells(sh.Rows.Count, 3).End(xlUp).Row
bSum = WorksheetFunction.Sum(sh.Range("B2:B" & lRowB))
cSum = WorksheetFunction.Sum(sh.Range("C2:C" & lRowC))
sh.Cells(lRowB + 2, 2) = bSum
sh.Cells(lRowC + 2, 3) = cSum
sh.Activate
Set sh = Nothing
End Sub
Remember: If you want to run same sub multiple time then you need clear totals otherwise it will add totals again again below of last totals.
Your code is perfect but there is only one error. You have initialized variable 'a' with 10000. Change it to 0.
a = 0
then your code will be perfect.
Add Totals to Multiple Columns
If you're not OP: It is easy to test the code. Open a new workbook and insert a module. Copy the code into the module. Uncomment the Sheet1 line, and outcomment the Revenue Summary line. In worksheet Sheet1 add some numbers in columns 2 and 3 and your ready.
Run only the insertTotals procedure. The calculateSumOfRange is called when needed.
Play with the constants in insertTotals and change the values in the columns. Add text, error values, booleans to see how the code doesn't break.
The issue with Application.Sum or WorksheetFunction.Sum is that it fails when there are error values in the range. That's what the calculateSumOfRange is preventing. If there is an error value, the loop approach is used. If not, then Application.Sum is the result.
You can use the calculateSumOfRange in Excel as a UDF. Just don't include the cell where the formula is and you're OK, e.g. =calculateSumOfRange(A1:B10).
The Code
Option Explicit
Sub insertTotals()
Const FirstRow As Long = 2 ' First Row of Data
Const LastRowCol As Long = 2 ' The column where the Last Row is calculated.
Const TotalsOffset As Long = 2 ' 2 means: 'data - one empty row - totals'
Dim Cols As Variant
Cols = Array(2, 3) ' add more
'With ThisWorkbook.Worksheets("Sheet1")
With Workbooks("Final_Files.xlsb").Worksheets("Revenue Summary")
' Define Last Row ('LastRow') in Last Row Column ('LastRowCol').
Dim LastRow As Long
LastRow = .Cells(.Rows.Count, LastRowCol).End(xlUp).Row
' Define Last Row Column Range ('rng').
Dim rng As Range
Set rng = .Range(.Cells(FirstRow, LastRowCol), _
.Cells(LastRow, LastRowCol))
Dim j As Long
' Validate Columns Array ('Cols').
If LBound(Cols) <= UBound(Cols) Then
' Iterate columns in Columns Array.
For j = LBound(Cols) To UBound(Cols)
' Use 'Offset' to define the current Column Range and write
' its calculated total below it.
.Cells(LastRow + TotalsOffset, Cols(j)).Value = _
calculateSumOfRange(rng.Offset(, Cols(j) - LastRowCol))
Next j
End If
End With
End Sub
Function calculateSumOfRange(SourceRange As Range) _
As Double
' Initialize error handling.
Const ProcName As String = "calculateSumOfRange"
On Error GoTo clearError ' Turn on error trapping.
' Validate Source Range.
If SourceRange Is Nothing Then
GoTo NoRange
End If
' Calculate Sum of Range.
Dim CurrentValue As Variant
CurrentValue = Application.Sum(SourceRange)
Dim Result As Double
If Not IsError(CurrentValue) Then
Result = CurrentValue
Else
Dim Data As Variant
If SourceRange.Rows.Count > 1 Or SourceRange.Columns.Count > 1 Then
Data = SourceRange.Value
Else
ReDim Data(1, 1)
Data(1, 1) = SourceRange.Value
End If
Dim i As Long
Dim j As Long
For i = 1 To UBound(Data, 1)
For j = 1 To UBound(Data, 2)
CurrentValue = Data(i, j)
If IsNumeric(CurrentValue) And _
Not VarType(CurrentValue) = vbBoolean Then
Result = Result + CurrentValue
End If
Next j
Next i
End If
' Write result and exit.
calculateSumOfRange = Result
GoTo ProcExit
' Labels
NoRange:
Debug.Print "'" & ProcName & "': No range (Nothing)."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
ProcExit:
End Function
The following code summs up all the rows under "B2" and "C2". Adapt it to your needs.
' Keep a reference to the worksheet
Dim ws as Worksheet
Set ws = Worksheets("Revenue Summary")
' This is how many rows there are.
Dim rowCount as Long
rowCount = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row-1
' This is the summation operation over each column
Dim b as Double, c as Double
b = WorksheerFunction.Sum(ws.Range("B2").Resize(rowCount,1))
c = WorksheerFunction.Sum(ws.Range("C2").Resize(rowCount,1))
' This writes the sum two cells under the last row.
ws.Range("B2").Cells(rowCount+2,1).Value = b
ws.Range("C2").Cells(rowCount+2,1).Value = c

how to merge cells with same value in one row

How do I merge cells with the same value and color in a row?
and the result should be :
I think you could try this:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, Value As Long
Dim Color As Double
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = LastRow To 2 Step -1
Value = .Range("A" & i).Value
Color = .Range("A" & i).Interior.Color
If .Range("A" & i - 1).Value = Value And .Range("A" & i - 1).Interior.Color = Color Then
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub
Copy Consecutive to One
Adjust the values in the constants section to fit your needs.
The image looks like you want all this to happen in the same column
of the same worksheet, which is adjusted in the constants section.
Before writing to Target Column (cTgtCol), the code will clear its
contents. Be careful not to lose data.
Colors are applied using a loop, which will slow down the fast array approach of copying the data.
The Code
Sub CopyConsecutiveToOne()
' Source
Const cSource As Variant = "Sheet1" ' Worksheet Name/Index
Const cSrcCol As Variant = "A" ' Column Letter/Number
Const cSrcFR As Long = 1 ' Column First Row Number
' Target
Const cTarget As Variant = "Sheet1" ' Worksheet Name/Index
Const cTgtCol As Variant = "A" ' Column Letter/Number
Const cTgtFR As Long = 1 ' Column First Row Number
Dim rng As Range ' Source Column Last Used Cell Range,
' Source Column Range, Target Column Range
Dim vntS As Variant ' Source Array
Dim vntT As Variant ' Target Array
Dim vntC As Variant ' Color Array
Dim i As Long ' Source Range/Array Row/Element Counter
Dim k As Long ' Target/Color Array Element Counter
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'On Error GoTo ProcedureExit
' In Source Worksheet
With ThisWorkbook.Worksheets(cSource).Columns(cSrcCol)
' Calculate Source Column Last Used Cell Range.
Set rng = .Find("*", , xlFormulas, xlWhole, xlByColumns, xlPrevious)
' Check if data in Source Column.
If Not rng Is Nothing Then ' Data found.
' Calculate Source Range.
Set rng = .Range(.Cells(cSrcFR), .Cells(rng.Row))
' Copy values from Source Range to Source Array.
vntS = rng
Else ' Data Not Found.
With .Cells(1)
MsgBox "No Data in column '" & .Split(.Address, "$")(1) & "'."
GoTo ProcedureExit
End With
End If
End With
' In Arrays
' Count the number of elements in Target/Color Array.
k = 1 ' The first element will be included before the loop.
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
End If
Next
' Write to Target/Color Arrays
' Resize Target/Color Arrays.
ReDim vntT(1 To k, 1 To 1)
ReDim vntC(1 To k, 1 To 1)
' Reset Counter
k = 1 ' The first element will be included before the loop.
' Write first value from Source Array to Target Array.
vntT(1, 1) = vntS(1, 1)
' Write first color value to Target Color Array.
vntC(1, 1) = rng.Cells(1, 1).Interior.Color
' Loop through elements of Source Array.
For i = 2 To UBound(vntS)
' Check if current value is different then the previous one.
If vntS(i, 1) <> vntS(i - 1, 1) Then
' Count element of Target/Color Array.
k = k + 1
' Write from Source Array to Target Array.
vntT(k, 1) = vntS(i, 1)
' Write color values from Source Range to Color Array.
vntC(k, 1) = rng.Cells(i, 1).Interior.Color
End If
Next
' All necessary data is in Target/Color Arrays.
Erase vntS
Set rng = Nothing
' In Target Worksheet
With ThisWorkbook.Worksheets(cTarget).Cells(cTgtFR, cTgtCol)
' Clear contents of range from Target First Cell to Target Bottom Cell.
.Resize(Rows.Count - .Row + 1).ClearContents
' Calculate Target Column Range.
Set rng = .Resize(k)
' Copy Target Array to Target Range.
rng = vntT
' Apply colors to Target Range.
With rng
' Loop through cells of Target Column Range.
For i = 1 To k
' Apply color to current cell of Target Range using the values
' from Color Array.
.Cells(i, 1).Interior.Color = vntC(i, 1)
Next
End With
End With
ProcedureExit:
With Application
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
End Sub
Create a custom function in Visual Basic Editor that will return to the color index of the cell:
Function COLOR(Target As Range)
COLOR = Target.Interior.ColorIndex
End Function
Then in the right column use a formula similar to this:
=IF(OR(A2<>A3,COLOR(A2)<>COLOR(A3)),1,0)
Then filter to show only 1's.

Using loops to copy and paste

I have a large set of duplicate data, I want to be able to copy each unique value and paste it twice into a new worksheet so A1 and A2 will be the same for the first value. Then for the next unique value I want A3 and A4 to be the same and so on until the end of the column. How do I do this? I'm assuming it will be some sort of for or do loop.
So assume Column C is on a different sheet, but I want the data to be simplified like this
You can use a collection, then input to other sheet.
Sheet 2 column C has the original data.
Sub UsingCollection()
Dim cUnique As Collection
Dim Rng As Range, r As Long
Dim Cell As Range
Dim sh As Worksheet
Dim vNum As Variant
Set sh = ThisWorkbook.Sheets("Sheet1")
Set ws = Sheets("Sheet2")
Set Rng = ws.Range("C2:C" & ws.Cells(ws.Rows.Count, "C").End(xlUp).Row)
Set cUnique = New Collection
On Error Resume Next
For Each Cell In Rng.Cells
cUnique.Add Cell.Value, CStr(Cell.Value)
Next Cell
On Error GoTo 0
For Each vNum In cUnique
r = Cells(Rows.Count, "A").End(xlUp).Row + 1
Range(Cells(r, 1), Cells(r + 1, 1)).Value = vNum
Next vNum
End Sub
Multi Unique Values
Carefully adjust the variables in the constants section. The first 7 variables should be self-explanatory.
cBlnTargetFirstRow set to True enables the calculation of the first row on the Target Worksheet e.g. if you want to append the data to the data already in that column.
cBlnTargetNewWorksheet set to True enables the output of the result in a new worksheet, which is added to the end.
cIntBuffer is an increment of the size of the Unique Array i.e. each time the array is full, that amount is added to its size.
'*******************************************************************************
' Purpose: In a column, copies unique values, from each cell a specific
' number of times, to another column.
'*******************************************************************************
Sub MultiUniqueValues()
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
On Error GoTo UnexpectedErr
Const cVntSource As Variant = "Sheet1" ' Source Worksheet Name/Index
Const cVntTarget As Variant = "Sheet1" ' Target Worksheet Name/Index
Const cLngSourceFR As Long = 1 ' Source First Row
Const cLngTargetFR As Long = 1 ' Target First Row
Const cVntSourceC As Variant = "C" ' Source Column Letter/Number
Const cVntTargetC As Variant = "A" ' Target Column Letter/Number
Const cIntRepeat As Integer = 2 ' Unique Values Repeat Count
Const cBlnTargetFirstRow As Boolean = False ' Target First Row Calculation
Const cBlnTargetNewWorksheet As Boolean = False ' Target Worksheet Creation
Const intBuffer As Long = 10 ' Unique Array Resize Buffer
Dim vntSource As Variant ' Source Array
Dim vntUni As Variant ' Unique Array
Dim vntTarget As Variant ' Target Array
Dim lng1 As Long ' Source Array Counter
Dim lng2 As Long ' Unique Array Counter, Repeat Counter
Dim lng3 As Long ' Unique Values Count(er), Target Array Counter
' Paste column range into one-based 2-dimensional (1B2D) Source Array.
With ThisWorkbook.Worksheets(cVntSource)
vntSource = .Range(.Cells(cLngSourceFR, cVntSourceC), _
.Cells(.Rows.Count, cVntSourceC).End(xlUp))
End With
' Try to write first non-empty row from 1B2D Source to 1B1D Unique Array.
For lng1 = 1 To UBound(vntSource)
If Not IsEmpty(vntSource(lng1, 1)) Then
ReDim vntUni(1 To intBuffer)
vntUni(1) = vntSource(lng1, 1)
lng3 = 1
Exit For
End If
Next
If lng1 = UBound(vntSource) + 1 Then GoTo SourceArrayErr ' No non-empty.
' Write the rest of the non-empty rows from 1B2D Source to 1B1D Unique Array.
For lng1 = lng1 + 1 To UBound(vntSource)
For lng2 = 1 To lng3
' Check if current row of Source Array is empty and check it against
' all values in current Unique Array.
If IsEmpty(vntSource(lng1, 1)) Or _
vntUni(lng2) = vntSource(lng1, 1) Then Exit For ' Match found.
Next ' Match not found i.e. "'counter' = 'end' + 1".
If lng2 = lng3 + 1 Then
lng3 = lng2 ' (lng3 + 1)
' Resize 1B1D Unique Array if full.
If (lng3 - 1) Mod intBuffer = 0 Then
ReDim Preserve vntUni(1 To UBound(vntUni) + intBuffer)
End If
vntUni(lng3) = vntSource(lng1, 1) ' Write row to Unique Array.
Else
End If
Next
Erase vntSource
' Resize 1B1D Unique Array i.e. truncate last empty rows.
ReDim Preserve vntUni(1 To lng3)
' Copy 1B1D Unique Array to 1B2D Target Array.
ReDim vntTarget(1 To lng3 * cIntRepeat, 1 To 1)
lng3 = 0
For lng1 = 1 To UBound(vntUni)
For lng2 = 1 To cIntRepeat
lng3 = lng3 + 1
vntTarget(lng3, 1) = vntUni(lng1)
Next
Next
Erase vntUni
' Note: To shorten the following code, an Object reference could have
' been implemented. Didn't wanna do that.
' Paste 1B2D Target Array into Target Range.
If cBlnTargetNewWorksheet Then ' Paste into range of new worksheet.
With ThisWorkbook.Worksheets(cVntTarget)
.Parent.Sheets.Add After:=.Parent.Sheets(Sheets.Count)
With .Parent.Worksheets(Sheets.Count) ' It is the ActiveSheet, now.
If cBlnTargetFirstRow Then ' Target first row calculation enabled.
If .Cells(.Rows.Count, cVntTargetC).End(xlUp).Row = 1 And _
IsEmpty(.Cells(.Cells(.Rows.Count, _
cVntTargetC).End(xlUp).Row, cVntTargetC)) Then
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
Else
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row + 1, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
End If
Else ' Target first row calculation disabled.
.Cells(cLngTargetFR, cVntTargetC).Resize(UBound(vntTarget)) _
= vntTarget
End If
End With
End With
Else ' Paste into range of specified worksheet.
With ThisWorkbook.Worksheets(cVntTarget)
If cBlnTargetFirstRow Then ' Target first row calculation enabled.
If .Cells(.Rows.Count, cVntTargetC).End(xlUp).Row = 1 And _
IsEmpty(.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC)) Then
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
Else
.Cells(.Cells(.Rows.Count, cVntTargetC).End(xlUp).Row + 1, _
cVntTargetC).Resize(UBound(vntTarget)) = vntTarget
End If
Else ' Target first row calculation disabled.
.Cells(cLngTargetFR, cVntTargetC).Resize(UBound(vntTarget)) _
= vntTarget
End If
End With
End If
Erase vntTarget
ProcedureExit:
With Application
.EnableEvents = True
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
Exit Sub
SourceArrayErr:
MsgBox "No data in Source Array."
GoTo ProcedureExit
UnexpectedErr:
MsgBox "An unexpected error occurred. Error: '" & Err.Number & "', " _
& Err.Description
GoTo ProcedureExit
End Sub
'*******************************************************************************

Import variable range into Array/Collection?

Is there any way to import a range that looks like this:
I'm trying to import a range with an undetermined number of rows and columns. As the 5th row indicates, the range that I wish to import has in the first column business names and in their subsequent columns, different iterations of the same business.
I've been thinking of using arrays but I can't see it being possible as I would have varying dimensions per element (eg. 3 dimensions for canadian tire and 2 dimensions for mercedes).
I've also thought of using collections/dictionaries but I stumble at using and understanding them.
Ultimately, my intentions are to loop the iterations from this range in a column and, if any of these iterations match a cell in my column, to write in an offset cell the first iteration (business name in bold).
Now, I know, I could do a two dimensional array from a range like this, with repeated first iterations (business name):
However, it's quite cumbersome to rewrite business names.
My code below for what I was using for the two dimensional array:
Option Explicit
Sub VendorFinder()
'variable declaration
Dim msg As String
Dim ans As Integer
Dim rng As Range
Dim DescRng As Range
Dim DescCol As Range
Dim VendorCol As Range
Dim j As Long
Dim Vendor As Variant
Dim wb As Workbook
Dim sFile As String
Dim myVendor As Variant
Dim FirstRow As Range
Dim VendorRng As Range
'import vendors
sFile = "Z:\Vendor List.xlsx"
Application.ScreenUpdating = False
Set wb = Application.Workbooks.Open(sFile)
Vendor = wb.Sheets(1).Range(Cells(1, 1), Cells(Cells(Rows.Count, 2).End(xlUp).Row, 2)).Value2
wb.Close False
Application.ScreenUpdating = True
On Error GoTo BadEntry
TryAgain:
'set columns
Set DescCol = Application.InputBox("Select Description Column", "Obtain Object Range", Type:=8)
Set VendorCol = Application.InputBox("Select Vendor Column", "Obtain Object Range", Type:=8)
Set FirstRow = Application.InputBox("Select First Row with Data", "Obtain Object Range", Type:=8)
'set ranges
Set DescRng = Range(Cells(FirstRow.Row, DescCol.Column), Cells(Cells(Rows.Count, DescCol.Column).End(xlUp).Row, DescCol.Column))
Set VendorRng = Range(Cells(FirstRow.Row, VendorCol.Column), Cells(Cells(Rows.Count, DescCol.Column).End(xlUp).Row, VendorCol.Column))
myVendor = VendorRng.Value2
For Each rng In DescRng
If Cells(rng.Row, VendorCol.Column).Value = "" Then
For j = LBound(Vendor) To UBound(Vendor)
If InStr(1, rng.Value, Vendor(j, 2), vbTextCompare) > 0 Then
myVendor(rng.Row - FirstRow.Row + 1, 1) = Vendor(j, 1)
Exit For
End If
Next j
End If
Next rng
VendorRng.Resize(UBound(myVendor) - LBound(myVendor) + 1, 1) = myVendor
Exit Sub
BadEntry:
msg = "You have clicked on cancel for one of the prompts."
msg = msg & vbNewLine
msg = msg & "Do you wish to try again?"
ans = MsgBox(msg, vbRetryCancel + vbExclamation)
If ans = vbRetry Then Resume TryAgain
End Sub
Thanks a lot!
I think I might have something simpler
Dim arr As New Collection, a
Dim var() As Variant
Dim i As Long
Dim lRows As Long, lCols As Long
Dim lRowCurrent As Long
Dim lCounter As Long
'Get the active range
Set rng = ActiveSheet.UsedRange
lRows = rng.Rows.Count
lCols = rng.Columns.Count
lRowCurrent = 0
'Loop thru every row
For i = 1 To lRows
' Read each line into an array
var() = Range(Cells(i, 1), Cells(i, lCols))
' Create a list of unique names only
On Error Resume Next
For Each a In var
arr.Add a, a
Next
'List all names
lCounter = arr.Count
For b = 1 To lCounter
Cells(lRowCurrent + b, 7) = arr(1)
Cells(lRowCurrent + b, 8) = arr(b)
Next
Set arr = Nothing
lRowCurrent = lRowCurrent + lCounter
Next
Try this:
Sub DoTranspose()
Dim r&, cnt&
Dim rng As Range, rngRow As Range, cell As Range
Set rng = Sheets("Source").Range("A1").CurrentRegion
r = 1
For Each rngRow In rng.Rows
cnt = WorksheetFunction.CountA(rngRow.Cells)
With Sheets("output").Cells(r, 1).Resize(cnt)
.Value = rngRow.Cells(1).Value
.Offset(, 1).Value = Application.Transpose(rngRow.Resize(, cnt).Value)
End With
r = r + cnt
Next
End Sub
Sample workbook.
This seems to be a simple un-pivot operation.
If you have Excel 2010+, you can use Power Query (aka Get&Transform in Excel 2016+), to do this.
Select a single cell in the table
Data / Get & Transform / From Range should select the entire table
Select the first column in the Query table.
Transform / Unpivot other columns
Delete the unwanted column
Save and Load
(Takes longer to type than to do)
This is the M Code, but you can do it all from the PQ GUI:
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", type text}, {"Column2", type text}, {"Column3", type text}, {"Column4", type text}}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"Column1"}, "Attribute", "Value"),
#"Removed Columns" = Table.RemoveColumns(#"Unpivoted Other Columns",{"Attribute"})
in
#"Removed Columns"
Original Data
Unpivoted
Range Array Array Range
A Picture is Worth a Thousand Words
The left worksheet is the initial worksheet, and the right the resulting one.
Since cBlnColors is set to True, several ranges are being colored to better understand how this code works and to indicate the upsides of having headers.
The light yellow color represents the data range, while the yellow color represents the rest of the off limits cells.
All not colored cells can be used without affecting the results in the right worksheet.
cBlnHeadersBelow set to True means that the data is above the headers (unusual), while set to False would mean that the data will be below the headers (as usual).
Headers Below Data with Colors
Another Thousand
The following picture shows the same code used with cBlnHeadersBelow set to False.
The yellow range spans down to the last row (not visible).
Again, all not colored cells can be used without affecting the results in the right worksheet.
Headers Above Data with Colors
The Code
Option Explicit
'*******************************************************************************
' Purpose: In a specified worksheet of a specified workbook, transposes a
' range of data (vertical table!?) to a two-column range in a newly
' created worksheet.
' Arguments (As Constants):
' cStrFile
' The path of the workbook file. If "", then ActiveWorkbook is used.
' cVarWs
' It is declared as variant to be able to use both, the title
' (a string e.g. "Sheet1") or the index (a positive whole number e.g. 1)
' of the worksheet. If "", then ActiveSheet is used.
' cStrTitle
' The contents of the first cell in the headers to be searched for.
' cBlnHeaders
' If True, USE headers.
' If False, do NOT use headers i.e. cBlnHeadersBelow has no effect and the
' first data found by searching by column from "A1" is used as first cell
' and the last found data on the worksheet is used for last cell.
' cBlnHeadersBelow
' If True, the data is ABOVE the headers (Data-Then-Headers).
' If False, the data is as usual BELOW the headers (Headers-Then-Data).
' cStrPaste
' The cell address of the first cell of the resulting range in the new
' worksheet.
' cBlnColors
' If True, and cBlnHeaders is True, then colors are being used i.e. one
' color for the data range, and another for off limits ranges.
' If True, and cBlnHeaders is False, all cells are off limits,
' so only the data range is colored.
' Returns
' A new worksheet with resulting data. No threat to the initial worksheet.
' If you don't like the result, just close the workbook.
'*******************************************************************************
Sub VendorFinder()
Application.ScreenUpdating = False
'***************************************
' Variables
'***************************************
Const cStrFile As String = "" ' "Z:\arrInit List.xlsx"
Const cVarWs As Variant = 1 ' "" for ActiveSheet.
Const cStrTitle As String = "Business" ' Contents of First Cell of Header
Const cBlnHeaders As Boolean = True ' True for Headers
Const cBlnHeadersBelow As Boolean = True ' True for Headers Below Data
Const cStrPaste As String = "A1" ' Resulting First Cell Address
Const cBlnColors As Boolean = True ' Activate Colors
Dim objWb As Workbook ' Workbook to be processed
Dim objWs As Worksheet ' Worksheet to be processed
Dim objTitle As Range ' First Cell of Header
Dim objFirst As Range ' First Cell of Data
Dim objLast As Range ' Last Cell of Data
Dim objResult As Range ' Resulting Range
Dim arrInit As Variant ' Array of Initial Data
Dim arrResult() As Variant ' Array of Resulting Data
Dim lngRows As Long ' Array Rows Counter
Dim iCols As Integer ' Array Columns Counter
Dim lngVendor As Long ' Array Data Counter, Array Row Counter
' ' Debug
' Const r1 As String = vbCr ' Debug Rows Separator
' Const c1 As String = "," ' Debug Columns Separator
'
' Dim str1 As String ' Debug String Builder
' Dim lng1 As Long ' Debug Rows Counter
' Dim i1 As Integer ' Debug Columns Counter
'***************************************
' Workbook
'***************************************
'On Error GoTo WorkbookErr
If cStrFile <> "" Then
Set objWb = Workbooks.Open(cStrFile)
Else
Set objWb = ActiveWorkbook
End If
'***************************************
' Worksheet
'***************************************
' On Error GoTo WorksheetErr
If cVarWs <> "" Then
Set objWs = objWb.Worksheets(cVarWs)
Else
Set objWs = objWb.ActiveSheet
End If
With objWs
' Colors
If cBlnColors = True Then
Dim lngData As Variant: lngData = RGB(255, 255, 153)
Dim lngOffLimits As Variant: lngOffLimits = RGB(255, 255, 0)
Else
.Cells.Interior.ColorIndex = xlNone
End If
' Assumptions:
' 1. Headers is a contiguous range.
' 2. The Headers Title is the first cell of Headers i.e. the first cell
' where cStrTitle is found while searching by rows starting from cell
' "A1".
' 3. The Headers Range spans from the Headers Title to the last cell,
' containing data, on the right.
' 4. All cells to the left and to the right of the Headers Range except
' for the cell adjacent to the right are free to be used i.e. no
' calculation is performed on them. If cBlnHeadersBelow is set to True,
' the cells below the Headers Range are free to be used. Similarly,
' if cBlnHeadersBelow is set to False the cells above are free to be
' used.
' 5. When cBlnHeadersBelow is set to True, the first row of data is
' calculated just using the column of the Headers Title
If cBlnHeaders = True Then ' USE Headers.
' Calculate Headers Title (using cStrTitle as criteria).
Set objTitle = .Cells _
.Find(What:=cStrTitle, After:=.Cells(.Rows.Count, .Columns.Count), _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext)
' Calculate initial first and last cells of data.
If cBlnHeadersBelow Then ' Headers are below data.
' Search for data in column of Headers Title starting from the first
' worksheet's row forwards to the row of Headers Title.
' When first data is found, the first cell is determined.
Set objFirst = .Range(.Cells(1, objTitle.Column), objTitle) _
.Find(What:="*", After:=.Cells(objTitle.Row, objTitle.Column), _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlNext)
' xlToRight, indicating that Headers Range is contiguous, uses the
' last cell of Headers Range while -1 sets the cells' row, one row above
' the Headers Title, resulting in the last cell range.
Set objLast = objTitle.End(xlToRight).Offset(-1, 0)
' Colors
If cBlnColors = True Then
.Cells.Interior.ColorIndex = xlNone
If objFirst.Row > 1 Then
.Range(.Cells(1, objFirst.Column), _
.Cells(objFirst.Row - 1, objLast.Column)) _
.Interior.color = lngOffLimits
End If
If objLast.Column < .Columns.Count Then
.Range(objTitle, .Cells(objTitle.Row, objLast.Column + 1)) _
.Interior.color = lngOffLimits
Else
.Range(objTitle, .Cells(objTitle.Row, objLast.Column)) _
.Interior.color = lngOffLimits
End If
.Range(objFirst, objLast).Interior.color = lngData
End If
Else ' Headers are above data (usually).
' 1 sets the cells' row, one row below the Headers Title
' resulting in the first cell range.
Set objFirst = objTitle.Offset(1, 0)
' Search for data in column of Headers Title starting from the last
' worksheet's row backwards to the row of Headers Title.
' When first data is found, the last row is determined and combined
' with the last column results in the last cell range.
Set objLast = .Cells( _
.Range(objTitle, .Cells(.Rows.Count, _
objTitle.End(xlToRight).Column)) _
.Find(What:="*", After:=objTitle, _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious) _
.Row, _
_
objTitle.End(xlToRight) _
.Column)
'Colors
If cBlnColors = True Then
.Cells.Interior.ColorIndex = xlNone
If objLast.Row < .Rows.Count Then
.Range(.Cells(objLast.Row + 1, objFirst.Column), _
.Cells(.Rows.Count, objLast.Column)) _
.Interior.color = lngOffLimits
End If
If objLast.Column < .Columns.Count Then
.Range(objTitle, .Cells(objTitle.Row, objLast.Column + 1)) _
.Interior.color = lngOffLimits
Else
.Range(objTitle, .Cells(objTitle.Row, objLast.Column)) _
.Interior.color = lngOffLimits
End If
.Range(objFirst, objLast).Interior.color = lngData
End If
End If
Else ' Do NOT use headers.
' Search for data in any cell from "A1" by column. When first data is
' found, the first cell is determined.
Set objFirst = _
.Cells _
.Find(What:="*", After:=.Cells(.Rows.Count, .Columns.Count), _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext)
' Last cell with data on the worksheet.
Set objLast = .Cells( _
_
.Cells _
.Find(What:="*", After:=.Cells(1, 1), _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByRows, SearchDirection:=xlPrevious) _
.Row, _
_
.Cells _
.Find(What:="*", After:=.Cells(1, 1), _
LookIn:=xlFormulas, Lookat:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious) _
.Column)
' Colors
If cBlnColors = True Then
.Cells.Interior.ColorIndex = xlNone
Range(objFirst, objLast).Interior.color = lngData
End If
End If
End With
'***************************************
' arrInit
'***************************************
' On Error GoTo arrInitErr
' Paste the values (Value2) of initial range into initial array (arrInit).
arrInit = Range(objFirst, objLast).Value2
' ' Debug
' str1 = r1 & "Initial Array (arrInit)" & r1
' For lng1 = LBound(arrInit) To UBound(arrInit)
' str1 = str1 & r1
' For i1 = LBound(arrInit, 2) To UBound(arrInit, 2)
' If i1 <> 1 Then
' str1 = str1 & c1
' End If
' str1 = str1 & arrInit(lng1, i1)
' Next
' Next
' Debug.Print str1
' Count data in arrInit.
For lngRows = LBound(arrInit) To UBound(arrInit)
For iCols = LBound(arrInit, 2) To UBound(arrInit, 2)
If arrInit(lngRows, iCols) <> "" Then
lngVendor = lngVendor + 1
End If
Next
Next
'***************************************
' arrResult
'***************************************
' On Error GoTo arrResultErr
ReDim arrResult(1 To lngVendor, 1 To 2) ' Resize resulting array (arrResult).
lngVendor = 0 ' Reset array data counter to be used as array row counter.
' Loop through arrInit and write to arrResult.
For lngRows = LBound(arrInit) To UBound(arrInit)
For iCols = LBound(arrInit, 2) To UBound(arrInit, 2)
If arrInit(lngRows, iCols) <> "" Then
lngVendor = lngVendor + 1
If iCols = 1 Then
arrResult(lngVendor, 1) = arrInit(lngRows, iCols)
Else
arrResult(lngVendor, 1) = arrResult(lngVendor - 1, 1)
End If
arrResult(lngVendor, 2) = arrInit(lngRows, iCols)
End If
Next
Next
Erase arrInit ' Data is in arrResult.
' ' Debug
' str1 = r1 & "Resulting Array (arrResult)" & r1
' For lng1 = LBound(arrResult) To UBound(arrResult)
' str1 = str1 & r1
' For i1 = LBound(arrResult, 2) To UBound(arrResult, 2)
' If i1 <> 1 Then
' str1 = str1 & c1
' End If
' str1 = str1 & arrResult(lng1, i1)
' Next
' Next
' Debug.Print str1
' Since there is only an infinite number of possibilities what to do with the
' resulting array, pasting it into a new worksheet has been chosen to be able
' to apply the bold formatting of the "Business Names" requested.
'***************************************
' New Worksheet
'***************************************
On Error GoTo NewWorksheetErr
Worksheets.Add After:=objWs
Set objResult = ActiveSheet.Range(Range(cStrPaste), _
Range(cStrPaste).Offset(UBound(arrResult) - 1, _
UBound(arrResult, 2) - 1))
With objResult
' Paste arrResult into resulting range (objResult).
.Value2 = arrResult
' Apply some formatting.
For lngRows = LBound(arrResult) To UBound(arrResult)
' If .Cells(lngRows, 1) = .Cells(lngRows, 2) Then ' Too slow!
If arrResult(lngRows, 1) = arrResult(lngRows, 2) Then
.Cells(lngRows, 1).Font.Bold = True
End If
Next
Erase arrResult ' Data is in objResult.
.Columns.AutoFit
End With
' To suppress the "Do you want to save changes you made to ... ?" - Alert:
objWb.Saved = True
'***************************************
' Clean Up
'***************************************
NewWorksheetExit:
Set objResult = Nothing
WorksheetExit:
Set objLast = Nothing
Set objFirst = Nothing
Set objTitle = Nothing
Set objWs = Nothing
WorkbookExit:
Set objWb = Nothing
Application.ScreenUpdating = True
Exit Sub
'***************************************
' Errors
'***************************************
WorkbookErr:
MsgBox "Workbook Error" & " (Error: '" & Err.Number & "')"
GoTo WorkbookExit
WorksheetErr:
MsgBox "Worksheet Error" & " (Error: '" & Err.Number & "')"
GoTo WorksheetExit
arrInitErr:
MsgBox "arrInit Error" & " (Error: '" & Err.Number & "')"
GoTo WorksheetExit
arrResultErr:
MsgBox "arrResult Error" & " (Error: '" & Err.Number & "')"
GoTo WorksheetExit
NewWorksheetErr:
MsgBox "New Worksheet Error" & " (Error: '" & Err.Number & "')"
GoTo NewWorksheetExit
End Sub
'*******************************************************************************
Extras
While testing the code, there were a little too many many worksheets in the workbook so I wrote this:
'*******************************************************************************
' Purpose: Deletes all Worksheets in the ActiveWorkbook except one.
' Danger: This code doesn't ask anything, it just does. In the end you will
' end up with just one worksheet (cStrWsExcept) in the workbook
' (cStrWbPath). If you have executed this code and the result is not
' satisfactory, just close the workbook and try again or don't. There
' will be no alert like "Do you want to save ..." because of the line:
' ".Saved = True" i.e. "objWb.Saved = True".
' Arguments (As Constants):
' cStrWbPath
' The path of the workbook to be processed. If "", then ActiveWorkbook is
' used.
' cStrWsExcept
' The worksheet not to be deleted. If "", then the Activesheet is used.
'*******************************************************************************
Sub DeleteWorksheetsExceptOne()
Const cStrWbPath = "" ' if "" then ActiveWorkbook
Const cStrWsExcept = "Sheet1" ' if "" then ActiveSheet
Dim objWb As Workbook
Dim objWsExcept As Worksheet
Dim objWsDelete As Worksheet
If cStrWbPath = "" Then
Set objWb = ActiveWorkbook
Else
Set objWb = Workbooks(cStrWbPath)
End If
With objWb
If cStrWsExcept = "" Then
Set objWsExcept = .ActiveSheet
Else
Set objWsExcept = .Worksheets(cStrWsExcept)
End If
' To suppress the "Data may exist in the sheet(s) selected for deletion.
' To permanently delete the data, press Delete." - Alert:
Application.DisplayAlerts = False
For Each objWsDelete In .Worksheets
If objWsDelete.Name <> objWsExcept.Name Then
objWsDelete.Delete
End If
Next
' To suppress the "Do you want to save changes you made to ... ?" - Alert:
.Saved = True
Application.DisplayAlerts = True
End With
End Sub
'*******************************************************************************

Resources