Code to Generate Sequential Code with a Prefix - excel

I have a small userform with 1 combobox, 2 textbox and 1 command button. Image is attached.
Also the image of worksheet is attached below.
Upon Initialization of Userform, combobox is populated with account heads listed in Table1.
Selection of item from combobox will populate the textbox with Account Code listed in Table1.
Group Head textbox will be entered manually.
Below is my code...
Private Sub ComboBox1_Change()
Dim ws As Worksheet, tbl As ListObject, rng As Range, cmb As ComboBox
Dim accountcode As String, rng1 As Range
Set ws = Sheets("Sheet1")
Set tbl = ws.ListObjects("Table1")
Set rng = tbl.ListColumns(1).DataBodyRange
Set rng1 = tbl.ListColumns(2).DataBodyRange
Me.TextBox1.Value = Application.WorksheetFunction.Index(rng, Application.WorksheetFunction.Match(Me.ComboBox1.Value, rng1, 0))
End Sub
Private Sub CommandButton1_Click()
Dim ws As Worksheet, tbl As ListObject, row As ListRow
Set ws = Sheets("Sheet1")
Set tbl = ws.ListObjects("Table2")
Set row = tbl.ListRows.Add
prefix = Me.TextBox1.Value & "-"
Dim NextNum As Long
Dim LastRow As Long, lRow As Long
Dim myArr() As Long
With Sheets("Sheet1")
'Find Last Row in Group Head Code Column
LastRow = .Cells(.Rows.Count, "E").End(xlUp).row
ReDim myArr(1 To LastRow)
' read all cells contents and convert them to array of numbers
For lRow = 5 To LastRow
If Mid(.Cells(lRow, 5), 4) <> "" Then
myArr(lRow) = CLng(Mid(.Cells(lRow, 5), 4))
End If
Next lRow
' find maximum value in array
NextNum = WorksheetFunction.Max(myArr)
End With
row.Range(1, 1).Value = Me.ComboBox1.Value
row.Range(1, 2).Value = prefix & NextNum + 1
row.Range(1, 3).Value = Me.TextBox2.Value
End Sub
Private Sub UserForm_Initialize()
Dim ws As Worksheet, tbl As ListObject, rng As Range, cmb As ComboBox
Set ws = Sheets("Sheet1")
Set tbl = ws.ListObjects("Table1")
Set rng = tbl.ListColumns(2).DataBodyRange
Set cmb = Me.ComboBox1
For Each rng In rng
cmb.AddItem rng.Value
Next rng
End Sub
The command button reads the value in Table 2, COlumn and 2, Generate the serial number and post the values in the Table.
What i want with the command button is, if i select any other head from the combobox, the code should read the value associated with that prefix and then generate the next serial number. Currently it is not reading the prefix.
Kindly advise what changes need to be made in my command button code to achieve this.
Thanks
Salman

I think the problem you're experiencing is that you're trying to use Sheet1 as both the output display and data storage system. Often this isn't a problem, but in your case, it's causing you to have to search Table2 each time an entry is made.
You'd be better off using a module-level variable in VBA to keep track of the incrementing number for each code. There's also no need to look up Table 1 each time a selection is made. You could either store the codes in another module-level variable or exploit the ability for ComboBoxes to have more than one column. In the sample below I've gone for the latter because it's easy to read the ListObject straight into the combo box List property - if you want to go the same route, then you'd need to change the combo box ColumnCount property to 2 and, if you want the codes to be invisible, change the ColumnWidths property to something like 0 pt;130 pt.
Your code, then, could look something like the below:
Option Explicit
Private mNextInc() As Long
Private Function IndexOf(val As String, arr As Variant) As Long
Dim i As Long
'Get array index of lookup item
For i = 1 To UBound(arr, 1)
If arr(i, 1) = val Then
IndexOf = i
Exit Function
End If
Next
End Function
Private Sub ComboBox1_Change()
Me.TextBox1.Text = Me.ComboBox1.Value
End Sub
Private Sub CommandButton1_Click()
Dim tbl As ListObject
Dim rng As Range
Dim v As Variant
With Me.ComboBox1
'Create the output array
v = Array(.Text, _
.Value & "-" & mNextInc(.ListIndex), _
Me.TextBox2.Text)
'Write new row to table
Set tbl = ThisWorkbook.Worksheets("Sheet1") _
.ListObjects("Table2")
tbl.Range.Activate
If tbl.InsertRowRange Is Nothing Then
Set rng = tbl.HeaderRowRange.Offset(tbl.ListRows.Count + 1)
Else
Set rng = tbl.InsertRowRange
End If
rng.Value = v
'Increment the digit of code
mNextInc(.ListIndex) = mNextInc(.ListIndex) + 1
End With
End Sub
Private Sub UserForm_Initialize()
Dim tbl As ListObject
Dim lRow As ListRow
Dim rng As Range
Dim inc As Long
Dim i As Long
'Populate the combobox
Set rng = ThisWorkbook.Worksheets("Sheet1") _
.ListObjects("Table1").DataBodyRange
Me.ComboBox1.List = rng.Value
'Set the increment values to 1
ReDim mNextInc(rng.Rows.Count - 1)
For i = 1 To UBound(mNextInc)
mNextInc(i) = 1
Next
'Find current max value for each group head code
Set tbl = ThisWorkbook.Worksheets("Sheet1") _
.ListObjects("Table2")
For Each lRow In tbl.ListRows
With lRow.Range
i = IndexOf(.Cells(1), rng.Value)
inc = CLng(Mid(.Cells(2), 4, Len(.Cells(2)) - 3)) + 1
End With
If inc > mNextInc(i) Then mNextInc(i) = inc
Next
'Set the combo box to first item
Me.ComboBox1.ListIndex = 0
End Sub

Related

Remove duplicate rows based on all columns via VBA

I found a great solution from this post: Removing duplicate rows after checking all columns
Sub Remove_DuplicateRows()
Dim intArray As Variant, i As Integer
Dim rng As Range
Dim ws As Worksheet
Call Open_Workbook
Set ws = Workbooks("Sales2021.xlsm").Sheets("Reporting Template")
ws.Activate
Set rng = ws.UsedRange.Rows
With rng
ReDim intArray(0 To .Columns.Count - 1)
For i = 0 To UBound(intArray)
intArray(i) = i + 1
Next i
.RemoveDuplicates Columns:=(intArray), Header:=xlYes
End With
End Sub
I tried the script, and wanted to adjust to my case: I want to delete all duplicated rows based on all columns except the first column (i.e., columns B to U). Should I use ws.Range("B2:U3000") instead of UsedRange?
You can either use ws.Range("B2:U3000") or below code
Set rng = ws.UsedRange.Offset(0, 1).Resize(ws.UsedRange.Rows.Count, ws.UsedRange.Columns.Count - 1)
The final code should look like this.
Sub Remove_DuplicateRows()
Dim intArray As Variant, i As Integer
Dim rng As Range
Dim ws As Worksheet
Call Open_Workbook
Set ws = Workbooks("Sales2021.xlsm").Sheets("Reporting Template")
ws.Activate
Set rng = ws.UsedRange.Offset(0, 1).Resize(ws.UsedRange.Rows.Count, ws.UsedRange.Columns.Count - 1)
With rng
ReDim intArray(0 To .Columns.Count - 1)
For i = 0 To UBound(intArray)
intArray(i) = i + 1
Next i
.RemoveDuplicates Columns:=(intArray), Header:=xlYes
End With
End Sub

Function returns temporary sheet

is this possible to create a function that returns temporary sheet?
Let's say I have got Sub as follow
Sub My_Sub()
Dim harm As Worksheet
Set harm = Sheets("my_sheet")
Dim lastRow As Long, arr
lastRow = harm.Range("A" & harm.Rows.Count).End(xlUp).Row
arr = harm.Range("T2:V" & lastRow).Value
MsgBox arr(2,5)+1
End Sub
Right now I'm working on harm = Sheets("my_sheet") and it loads whole sheet. Now I want to select part of that sheet and do the same operations so I wanted to write a function that will create temporary sheet, return it so in My_Sub I would have Set harm = ReturnSheet().
Is it possible? I want to load pseudo sheet from function, so I don't need to change anything in My_Sub (I mean those Ranges with column letter indexes).
Function ReturnSheet() As Worksheet
Dim Rng As Range
Dim lastRow As Long
Dim lastCol As Long
Set Rng = Selection
lastRow = Selection.Rows.Count
lastCol = Selection.Columns.Count
ReturnSheet.Range("A2").Resize(lastRow, lastCol).Value = Rng
End Function
Right now I'm getting Object variable or with block variable not set at ReturnSheet.Range("A2").Resize(lastRow, lastCol).Value = Rng
Try using the next Function. It returns a range meaning the selected cells without their first row:
Function ReturnRange(Optional boolAllRange As Boolean = False) As Range
Dim rng As Range: Set rng = Selection
If rng.rows.count = 1 Then Exit Function
If boolAllRange Then
Set ReturnRange = rng
Else
Set ReturnRange = rng.Offset(1).Resize(rng.rows.count - 1, rng.Columns.count)
End If
End Function
You can test it using the next Sub:
Sub testReturnRange()
Dim rng As Range
Set rng = ReturnRange 'eliminating the header
If Not rng Is Nothing Then Debug.Print rng.Address
Set rng = ReturnRange(True) 'header inclusive...
If Not rng Is Nothing Then Debug.Print rng.Address
End Sub

Replace Headers name in the first row from the cells column of another sheet

I am trying to achieve the following automation in VBA. I have different Sheets with wrong headers. I have another sheet called "HeadersMap", which contains the list of all Sheets's correct headers. What I want to do is, if I open a "Sheet1" then the code should go to the "HeadersMap" sheet > check the opened sheet name in the "SheetNames" column > check the Original header in "OriginalHeaders" column and copy correct header name from the "Correct Headers" column and replace the headers in the "Sheet1". And similarly , if I open "Sheet2", it should do the same.
"SHEET1"
A
B
C
1
aplpe
baanann
Roange
2
3
SHEET "HEADERSMAP"
A
B
C
1
SheetNames
OriginalHeaders
CorrectHeaders
2
Sheet1
aplpe
Apple
3
Sheet1
baanann
Banana
4
Sheet1
Roange
Orange
5
Sheet2
sgura
Sugar
6
Sheet2
Jggaery
Jaggery
7
Sheet3
Dtergetn
Detergent
8
Sheet3
poas
Soap
9
Sheet3
Lfua
Lufa
Desired Result "SHEET1"
A
B
C
1
Apple
Banana
Orange
2
3
Try,
Sub test()
Dim Ws As Worksheet
Dim vDB As Variant
Dim rngHeader As Range
Dim i As Integer
Set Ws = Sheets("HEADERSMAP")
vDB = Ws.Range("a1").CurrentRegion
For i = 2 To UBound(vDB, 1)
If isHas(vDB(i, 1)) Then
Set Ws = Sheets(vDB(i, 1))
Set rngHeader = Ws.Rows(1)
rngHeader.Replace vDB(i, 2), vDB(i, 3)
End If
Next i
End Sub
Function isHas(v As Variant) As Boolean
Dim Ws As Worksheet
For Each Ws In Worksheets
If Ws.Name = v Then
isHas = True
Exit Function
End If
Next Ws
End Function
Correct Headers
Edit
After reading your comment, it may be best to copy the complete code to the ThisWorkbook module (if you insist on this functionality). There is no need for adding another module.
It is assumed that the data in worksheet HeadersMap starts in cell A1.
Standard Module e.g. Module1
Option Explicit
Sub correctHeaders(ws As Worksheet)
Const sName As String = "HeadersMap"
Const sFirst As String = "A1"
Dim rg As Range
Dim Data As Variant
Set rg = ThisWorkbook.Worksheets(sName).Range(sFirst).CurrentRegion
If IsNumeric(Application.Match(ws.Name, rg.Columns(1), 0)) Then
Data = rg.Value
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim Result() As Variant
Dim r As Long, j As Long
For r = 1 To UBound(Data, 1)
If StrComp(Data(r, 1), ws.Name, vbTextCompare) = 0 Then
j = j + 1
ReDim Preserve Result(1 To 2, 1 To j)
Result(1, j) = Data(r, 2)
Result(2, j) = Data(r, 3)
End If
Next r
If j > 0 Then
Set rg = ws.UsedRange.Rows(1)
Data = rg.Value
Dim cIndex As Variant
For j = 1 To j
cIndex = Application.Match(Result(1, j), Data, 0)
If IsNumeric(cIndex) Then
Data(1, cIndex) = Result(2, j)
End If
Next j
rg.Value = Data
End If
End If
End Sub
Additional Functionality (you have to run it)
Sub correctHeadersApply
Dim ws As Worksheet
For Each ws in Thisworkbook.Worksheets
correctHeaders ws
Next ws
End Sub
ThisWorkbook Module
Option Explicit
Private Sub Workbook_Open()
correctHeaders ActiveSheet
End Sub
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
If Sh.Type = xlWorksheet Then
correctHeaders Sh
End If
End Sub
Bare minimum would probably be putting this in ThisWorkbook:
Private Sub Workbook_NewSheet(ByVal Sh As Object)
Dim targetRange As Range, i As Long
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = Sh.NAME Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
If the data is looking like your examples.
Later you might want ot change Range("A1:A9") to look for the last row, and Offset(, 2) to maybe Offset(, 1) since the "OriginalHeaders" column is superflous in reality.
The Module version would be something like:
Sub headers()
Dim targetRange As Range, i As Long, Sh As Worksheet
Set Sh = Worksheets(InputBox("Enter name of sheet"))
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = Sh.NAME Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
That is if the name of the sheet and the item in the list correlate.
You could set a second variable with a second inputbox, and replace Sh.NAME to select from the list manually.
Like so:
Sub headers()
Dim targetRange As Range, i As Long, Sh As Worksheet, name As String
Set Sh = Worksheets(InputBox("Enter name of sheet"))
name = InputBox("Enter name from map")
Set targetRange = Worksheets("HEADERSMAP").Range("A1:A9")
i = 1
For Each entry In targetRange
If entry.Value = name Then
Sh.Cells(1, i) = entry.Offset(, 2).Value
i = i + 1
End If
Next
End Sub
Then you can manually type witch sheet get what headers, if you like to do that.

Filter a listbox based on combobox

I am working on filtering a listbox based on the combobox selection.
Currently my codes look something like this.
Private Sub OEMNumberComboBox_Change()
Dim database(1 To 100, 1 To 7)
Dim i As Integer
Dim My_range As Integer
Dim colum As Byte
On Error Resume Next
Sheet7.Range("A1").AutoFilter field:=3, Criteria1:=Me.OEMNumberComboBox.Value
For i = 2 To Sheet7.Range("A100000").End(xlUp).Row
If Sheet7.Cells(i, 3) = Me.OEMNumberComboBox Then
My_range = My_range + 1
For colum = 1 To 7
database(My_range, colum) = Sheet7.Cells(i, colum)
Next colum
End If
Next i
ListBox1.List = database
End Sub
and the below during the intialisation
Sub Available_Stocks()
Application.ScreenUpdating = False
Dim invd_sh As Worksheet
Set invd_sh = ThisWorkbook.Sheets("Inventory")
Dim lr As Integer
lr = Application.WorksheetFunction.CountA(invd_sh.Range("A:A"))
If lr = 1 Then lr = 2
With Me.ListBox1
.ColumnCount = 9
.ColumnHeads = True
.ColumnWidths = "50,60,60,350,50,0,0,50,50"
.RowSource = "Inventory!A2:I" & lr
End With
End Sub
with the above codes it does filter the range but it is not reflected on the listbox and I am not sure what is wrong with the code.
It is exact copy of the online codes but i have made a slight modification (so that it is filtering column C).
user interface/objects
Update
Private Sub UserForm_Initialize()
'add column of data from spreadsheet to your userform ComboBox
OEMNumberComboBox.List = Sheets("Sheet1").Range("C1:C50").Value
End Sub
I have added the above code to populate the combobox but it sill shows one cell inside the listbox
For demonstration purpose, let's say your worksheet looks like the below and I want to populate all cells where the value of column C is 1
Logic:
Declare a Variant array.
Filter on column C with the relevant value from the combobox.
Loop through the Areas of the filtered range and populate the array.
Assign the array to the Listbox's .List.
Code:
Is this what you are tying? I have commented the code so that you should not have a problem understanding it. But if you do, then simply ask.
Option Explicit
Dim ws As Worksheet
Dim lrow As Long
Dim i As Long, j As Long
Private Sub UserForm_Initialize()
'~~> Set this to the relevant worksheet
Set ws = Sheet1
'~~> Set the listbox column count
ListBox1.ColumnCount = 8
Dim col As New Collection
Dim itm As Variant
With ws
'~~> Get last row in column C
lrow = .Range("C" & .Rows.Count).End(xlUp).Row
'~~> Create a unique list from column C values
On Error Resume Next
For i = 2 To lrow
col.Add .Range("C" & i).Value2, CStr(.Range("C" & i).Value2)
Next i
On Error GoTo 0
'~~> Add the item to combobox
For Each itm In col
OEMNumberComboBox.AddItem itm
Next itm
End With
End Sub
Private Sub CommandButton1_Click()
'~~> If nothing selected in the combobox then exit
If OEMNumberComboBox.ListIndex = -1 Then Exit Sub
'~~> Clear the listbox
ListBox1.Clear
Dim DataRange As Range, rngArea As Range
Dim DataSet As Variant
With ws
'~~> Remove any filters
.AutoFilterMode = False
'~~> Find last row in Col C
lrow = .Range("C" & .Rows.Count).End(xlUp).Row
'~~> Filter on the relevant column
With .Range("C1:C" & lrow)
.AutoFilter Field:=1, Criteria1:=OEMNumberComboBox.Value
On Error Resume Next
Set DataRange = .Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow
On Error GoTo 0
End With
'~~> Check if the autofilter returned any results
If Not DataRange Is Nothing Then
'~~> Instead of using another object, I am reusing the object
Set DataRange = .Range("A2:G" & lrow).SpecialCells(xlCellTypeVisible)
'~~> Create the array
ReDim DataSet(1 To DataRange.Areas.Count + 1, 1 To 8)
j = 1
'~~> Loop through the area and store in the array
For Each rngArea In DataRange.Areas
For i = 1 To 8
DataSet(j, i) = rngArea.Cells(, i).Value2
Next i
j = j + 1
Next rngArea
'~~> Set the listbox list
ListBox1.List = DataSet
End If
'~~> Remove any filters
.AutoFilterMode = False
End With
End Sub
In Action:

Add to Listbox if cell value contains specific string

I am trying to add data to a Listbox on a Userform, based on the value of the the Cell in column C of the range that is searched. If the cell in column C contains a certain string I would like it to be added to the Listbox.
The below code is as far as I have got but it is returning an empty Listbox with no error.
Private Sub OptionButton12_Click()
Dim I As Integer
Dim lastRow As Integer
Dim searchString As String
searchString = "LISTBOXENTRY"
With ThisWorkbook.Sheets("Sheet1")
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Plybooks.ListBox1.Clear
For I = 1 To lastRow
If Cells(I, 3).Value = searchString Then
Plybooks.ListBox1.AddItem Range("A" & I)
End If
Next I
End Sub
Try using the script below and please let me know if it works!
based on your script above, I assumed some of the dataframe dimensions. please let me know if it is not correct so I can tweak it.
I assumed you are working on first sheet (sheets(1)), and col C is the column you are using for the value check against the "searchString" variable. (if true, append the value in listbox1)
Thanks
Private Sub OptionButton12_Click()
Dim lastRow As Integer
Dim searchString As String
Dim wb As Workbook
Dim sRng As Range
Dim cel As Range
'assign current wb into wb workbook object
Set wb = ThisWorkbook
'assign str you want to search into variable
searchString = "LISTBOXENTRY"
'find last row number in colC (3) using crow function. (assuming you want to do a check on every cell listed in column C)
lastRow = crow(1, 3)
plybooks.listbox1.Clear
'assign range object using dataframe dimensions based on row 1 col C (lbound), to lastrow col3 (ubound)
With wb.Sheets(1)
Set sRng = .Range(.Cells(1, 3), .Cells(trow, 3))
End With
'loops through each cel
For Each cel In sRng
If cel.Value = searchString Then
'adds item into listbox1 if conditional statement is True
plybooks.listbox1.AddItem Item:=cel.Value
Else
End If
Next cel
End Sub
Private Function crow(s As Variant, c As Integer)
crow = Sheets(s).Cells(Rows.Count, c).End(xlUp).Row
End Function
Added cell values in ranges over multiple sheets if cell contains certain value, using the following:
Public Sub PlybookListbox()
'Clear fields before start
Plybooks.ListBox1.MultiSelect = 0
Plybooks.ListBox1.Clear
Plybooks.ListBox1.Value = ""
Plybooks.ListBox1.MultiSelect = 2
Dim AllAreas(2) As Range, Idx As Integer, MyCell As Range, TargetRange As Range
Dim lastrowFrontWing As Long
Dim lastrowNose As Long
Dim lastrowBargeboard As Long
lastrowFrontWing = Worksheets("Front Wing").Cells(Rows.Count, 2).End(xlUp).Row
lastrowNose = Worksheets("Nose").Cells(Rows.Count, 2).End(xlUp).Row
lastrowBargeboard = Worksheets("Bargeboard & SPV").Cells(Rows.Count, 2).End(xlUp).Row
Set AllAreas(0) = Worksheets("Front Wing").Range("c6:c" & lastrowFrontWing)
Set AllAreas(1) = Worksheets("Nose").Range("c6:c" & lastrowNose)
Set AllAreas(2) = Worksheets("Bargeboard & SPV").Range("c6:c" & lastrowBargeboard)
Plybooks.ListBox1.Clear
For Idx = 0 To 2
For Each MyCell In AllAreas(Idx).Cells
If InStr(1, MyCell.Value, "(FS)") > 0 Then
Plybooks.ListBox1.AddItem MyCell.Value
End If
Next MyCell
Next Idx
End Sub

Resources