Merging two identical modules in Excel VBA - excel

Could help me merge these two modules, so I can use them more than only one time?
First module:
Private Sub UserForm_Initialize()
Dim ultimaLin As Long, area As New Collection
Dim Value As Variant, temp() As Variant
On Error Resume Next
ultimaLin = Sheets("DBTemp").Range("A" & Rows.Count).End(xlUp).Row
temp = Sheets("DBTemp").Range("A2:A" & ultimaLin).Value
For Each Value In temp
If Len(Value) > 0 Then area.Add Value, CStr(Value)
Next Value
For Each Value In area
titulo_livro.AddItem Value
Next Value
Set area = Nothing
End Sub
Second module:
Private Sub UserForm_Initialize()
Dim ultimaLin As Long, area As New Collection
Dim Value As Variant, temp() As Variant
On Error Resume Next
ultimaLin = Sheets("DBTemp").Range("B" & Rows.Count).End(xlUp).Row
temp = Sheets("DBTemp").Range("B2:B" & ultimaLin).Value
For Each Value In temp
If Len(Value) > 0 Then area.Add Value, CStr(Value)
Next Value
For Each Value In area
autor_livro.AddItem Value
Next Value
Set area = Nothing
End Sub
As you can see it, they are basically the same thing, but in the second one I want to reproduce the obtained result in another range.
Thanks!

You can factor out the common parts of the code into re-usable methods.
Example Form module code:
Private Sub UserForm_Initialize()
With ThisWorkbook.Sheets("DBTemp")
FillFromRange .Range("A2:A" & .Cells(.Rows.Count, "A").End(xlUp).Row), _
titulo_livro
End With
End Sub
In a regular module:
Sub FillFromRange(rng As Range, ctrl As Object)
Dim v
For Each v In UniquesFromRange(rng)
ctrl.AddItem v
Next v
End Sub
Function UniquesFromRange(rng As Range)
Dim col As New Collection, data, v
data = rng.Value
For Each v In data
If Len(v) > 0 Then
On Error Resume Next
col.Add v, CStr(v)
On Error GoTo 0
End If
Next v
Set UniquesFromRange = col
End Function

Related

Command button press cycles through a desired range

I'm trying to write code to where on each command button press, the current time is put into the first cell in a range. Then on the next button click, the next cell in the range is filled with the current time, and so on. I cant figure out how to cycle through the desired range and place a time value at that cell on each button press.
I have a basic double For loop that goes through the entire range I want and populates all cells with the current time at once. I only want one cell to populate at a time with the current time on each button click, and I cant figure out how for the life of me.
code so far:
Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer
For i = 6 To 115
For j = 3 To 5
Cells(i, j).Value = Time
Next j
Next i
End Sub
I understand it like that
Private Sub CommandButton1_Click()
Dim rg As Range
Set rg = Range("C6:E115")
Dim sngCell As Range
For Each sngCell In rg
If Len(sngCell.Value) = 0 Then
sngCell.Value = Time
Exit For
End If
Next
End Sub
Update:
This solution should be faster but I think it will not be noticeable.
Private Sub CommandButton1_Click()
Dim rg As Range
Set rg = Union(Range("C5"), Range("C6:E115"))
Dim nextCell As Range
Set nextCell = rg.Find("")
If Not nextCell Is Nothing And nextCell.Address <> "$C$5" Then
nextCell.Value = Time
End If
End Sub
this should do the trick:
Option Explicit
Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer
Dim emptyCellFound As Boolean
Dim c As Range
emptyCellFound = False 'not really needed. just for clarity
For i = 6 To 115
For j = 3 To 5
Set c = Cells(i, j)
If c.Value = "" And Not emptyCellFound Then
c.Value = Time
emptyCellFound = True
End If
Next j
Next i
End Sub
This works if any value is applied in any cell below row 114
Private Sub CommandButton1_Click()
On Error Resume Next
[C6:E115].SpecialCells(xlCellTypeBlanks).Cells(1).Value = Time
On Error GoTo 0
End Sub
..without a value below row 114 this works
Private Sub CommandButton1_Click()
On Error GoTo weird
[C6:E115].SpecialCells(xlCellTypeBlanks).Cells(1).Value = Time
Exit Sub
weird:
If [C115].End(xlUp).Row > 6 Then [C115].End(xlUp).Offset(1).Value = Time
On Error GoTo 0
End Sub

Remove duplicate form combobox

I am working on a sheet that have day to day sales data. I need to summaries the data between a specific date. for this I want to use a user form with 2 combo box (I have never worked with user forms & controls ever before). I added the items into combo box by using below codes -
Private Sub UserForm_Initialize()
ComboBox1.RowSource = "A2:A6724"
ComboBox2.RowSource = "A2:A6724"
End Sub
this worked fine. But here is a problem that it is repeating the same items many time as there are many transactions in same date in the sheet.
To solve this issue I search help in internet & found a procedure, I modify that and used in my code. that's working correctly but it also has a little problem that as I click on a date from drop down list of combo box it changes the date format (i.e. if I select 10/12/2016 it shows 12-oct-2016 but it should be 10-dec-2016)
here is the code I modify actually I don't know what it does but I think is will work for me-
Private Sub UserForm_Initialize()
'ComboBox1.RowSource = "A2:A6724"
'ComboBox2.RowSource = "A2:A6724"
Dim Coll As Collection, cell As Range, LastRow As Long
Dim blnUnsorted As Boolean, i As Integer, temp As Variant
Dim SourceSheet As Worksheet
Set SourceSheet = Worksheets("Sheet1")
LastRow = SourceSheet.Cells(Rows.Count, 1).End(xlUp).Row
On Error Resume Next
Set Coll = New Collection
With ComboBox1
.Clear
For Each cell In SourceSheet.Range("A2:A" & LastRow)
If Len(cell.Value) <> 0 Then
Err.Clear
Coll.Add cell.Text, cell.Text
If Err.Number = 0 Then .AddItem cell.Text
End If
Next cell
End With
Set SourceSheet = Worksheets("Sheet1")
LastRow = SourceSheet.Cells(Rows.Count, 1).End(xlUp).Row
On Error Resume Next
Set Coll = New Collection
With ComboBox2
.Clear
For Each cell In SourceSheet.Range("A2:A" & LastRow)
If Len(cell.Value) <> 0 Then
Err.Clear
Coll.Add cell.Text, cell.Text
If Err.Number = 0 Then .AddItem cell.Text
End If
Next cell
End With
Set Coll = Nothing
Set SourceSheet = Nothing
End Sub
I will be greatly Thankful for any help.
Try following code, that use a dictionary.
Public dU1 As Object, cU1 As Variant, iU1 As Long, lrU As Long
Private Sub UserForm_Initialize()
Dim i As Integer
Set dU1 = CreateObject("Scripting.Dictionary")
lrU = Worksheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
cU1 = Worksheets("Sheet1").Range("A2:A" & lrU) 'Starts in second row. First row left for titles
For iU1 = 1 To UBound(cU1, 1)
dU1(cU1(iU1, 1)) = 1
Next iU1
'now dU1 has unique values from column A
For i = 0 To dU1.Count - 1
ComboBox1.AddItem dU1.Keys()(i) 'Load Combobox1 with unique values from Column A
Next
End Sub
Private Sub ComboBox1_Change()
Dim lLastRow As Long
Dim i As Integer
ComboBox2.Clear
For i = 0 To dU1.Count - 1
If CDate(ComboBox1.Value) < CDate(dU1.Keys()(i)) Then
ComboBox2.AddItem dU1.Keys()(i) 'Load Combobox2
End If
Next
End Sub

Extract unique distinct list from two columns

So I am trying to create a combined list from two separate columns by omitting the duplicate items. I have searched and found a formula that combines the list this way by going through one column at a time.
But I want to combine the columns like this:
where it goes through each row first.
Is there a formula or VBA code that does that? Thank you.
EDIT: This is just a way to show my request. The color was added to show how the combined list is sorted, it is not part of the request. The actual lists are each about 500 rows long consisting of 9+ digit ID numbers.
This will put the unique words in the order you want.
Sub foo()
Dim rng As Range
Dim ws As Worksheet
Dim i&, j&, t&
Dim dict As Object
Dim iArr() As Variant
Dim oarr() As Variant
Dim itm As Variant
Set dict = CreateObject("Scripting.Dictionary")
Set ws = ActiveSheet
With ws
Set rng = .Range("A:B").Find("*", .Range("A1"), , , , xlPrevious)
If Not rng Is Nothing Then
iArr = .Range(.Cells(2, 1), .Cells(rng.Row, 2)).Value
For i = LBound(iArr, 1) To UBound(iArr, 1)
For j = LBound(iArr, 2) To UBound(iArr, 2)
If iArr(i, j) <> "" Then
On Error Resume Next
dict.Add iArr(i, j), iArr(i, j)
On Error GoTo 0
End If
Next j
Next i
End If
'If your dataset is not that large <30,000, then you can use it directly with transpose
.Range("C2").Resize(dict.Count) = Application.Transpose(dict.items)
'If your data is large then you will want to put it in a one dimensional array first
'just uncomment the below and comment the one line above
' ReDim oarr(1 To dict.Count, 1 To 1)
' t = 1
' For Each itm In dict.keys
' oarr(t, 1) = dict(itm)
' t = t + 1
' Next itm
' Range("C2").Resize(dict.Count) = oarr
End With
End Sub
UDF solution. Using your provided sample data, put this formula in cell I2 and copy down =UnqList(ROW(I1),$G$2:$H$6) or =UnqList(ROW(I1),$G$2:$G$6,$H$2:$H$6) (it can be either because the two or more lists might not be next to each other and the UDF accounts for that)
Public Function UnqList(ByVal lIndex As Long, ParamArray rLists() As Variant) As Variant
Dim i As Long, j As Long
Dim vList As Variant
Dim cUnq As Collection
Dim lMaxRow As Long, lMaxCol As Long
If lIndex <= 0 Then
UnqList = CVErr(xlErrRef)
Exit Function
End If
For Each vList In rLists
If TypeName(vList) <> "Range" Then
UnqList = CVErr(xlErrRef)
Exit Function
Else
If vList.Rows.Count > lMaxRow Then lMaxRow = vList.Rows.Count
If vList.Columns.Count > lMaxCol Then lMaxCol = vList.Columns.Count
End If
Next vList
Set cUnq = New Collection
For i = 1 To lMaxRow
For j = 1 To lMaxCol
For Each vList In rLists
If i <= vList.Rows.Count And j <= vList.Columns.Count Then
On Error Resume Next
cUnq.Add vList.Cells(i, j).Value, CStr(vList.Cells(i, j).Value)
On Error GoTo 0
If lIndex = cUnq.Count Then
UnqList = cUnq(cUnq.Count)
Set cUnq = Nothing
Exit Function
End If
End If
Next vList
Next j
Next i
UnqList = CVErr(xlErrRef)
Set cUnq = Nothing
End Function
You can use my Duplicate Master addin available via my profile.
Advantages are that the addin provides options to
ignore capitilisation
ignore whitespace
run RegExp replacements (advanced)
further options for deletinf, highlighting, selecting duplicates etc

Excel - VBA Removing Duplicates from Comboboxes

I am trying to create a subroutine to delete duplicates out of comboboxes. I input a number in place of X when I called the subroutine. I keep getting an error that tells me "Object Required" when i get to the subroutine. I know that means that something is not being properly initialized, but I cannot figure out how to fix my issue. Any help would be greatly appreciated. Thank you.
Private Sub UserForm_Initialize()
'ComboBox Populate
Dim rngNext As Range
Dim myRange As Range
Dim C As Integer
With Sheets("KEY")
Set rngNext = .Range("B500").End(xlUp).Offset(1, 0)
End With
rngNext.Select
Set myRange = Range("B2", rngNext)
With ComboBox1
For Each rngNext In myRange
If rngNext <> "" Then .AddItem rngNext
Next rngNext
End With
Call RemoveDuplicates(1)
End sub
Private Sub RemoveDuplicates(X)
'Remove Duplicates
Dim i As Long
Dim j As Long
With "ComboBox" & X
For i = 0 To .ListCount + 1 'Getting object required error in this line
For j = .ListCount To (i + 1) Step -1
If .List(j) = .List(i) Then
.RemoveItem j
End If
Next
Next
End With
End Sub
Final Code
Everything works great for removing duplicates.
Public allCBoxes As Collection
Private Sub UserForm_Initialize()
Set allCBoxes = New Collection
allCBoxes.Add ComboBox1
'ComboBox Populate
Dim rngNext As Range
Dim myRange As Range
Dim C As Integer
With Sheets("KEY")
Set rngNext = .Range("B500").End(xlUp).Offset(1, 0)
End With
rngNext.Select
Set myRange = Range("B2", rngNext)
With ComboBox1
For Each rngNext In myRange
If rngNext <> "" Then .AddItem rngNext
Next rngNext
End With
Call RemoveDuplicates(1)
End sub
Private Sub RemoveDuplicates(X)
'Remove Duplicates
Dim i As Long
Dim j As Long
With allCBoxes(X)
For i = 0 To .ListCount + 1
For j = .ListCount -1 To (i + 1) Step -1
If .List(j) = .List(i) Then
.RemoveItem j
End If
Next
Next
End With
End Sub
You get an error because you're passing a string, not an object.
Although intuitively you can think that:
"ComboBox" & X
will become, for example if x = 5,
ComboBox5
you're wrong because you're actually building a string:
"ComboBox5"
And, clearly, if you call a method of a ComboBox object on a String, you will be prompted of "Object Required".
What you want to do is impossible in VBA, where you cannot define variable names at run-time (i.e. ComboBox & X, even if not "as string", will not reference the variable ComboBox5). To reach what you want, I suggest to create a public collection:
Dim allCBoxes As Collection
then to populate it on the main procedure:
Set allCBoxes = New Collection
allCBoxes.Add ComboBox1
allCboxes.Add ComboBox2
'etc.
and finally recovering the "Xth" combobox like this:
With allCBoxes(X)
End With
If you want to reference a control using its string name, use the Controls function.
Such as:
With Controls("Combobox" & X)
Does that resolve the problem?
As mentioned in my comment above, here's a different approach towards solving the underlying problem: needing a combobox without duplicate values. This method uses a Dictionary object.
Let me know if you can adapt it to your needs, and if it works.
Private Sub UserForm_Initialize()
Dim oDictionary As Object
Dim strCellContent As String
Dim rngComboValues As Range
Dim rngCell As Range
Set rngComboValues = Range("A1:A26")
Set oDictionary = CreateObject("Scripting.Dictionary")
For Each rngCell In rngComboValues
strCellContent = rngCell.Value
If Not oDictionary.exists(strCellContent) Then
oDictionary.Add strCellContent, 0
End If
Next rngCell
For Each itm In oDictionary.keys
Me.ComboBox1.AddItem itm
Next itm
Set oDictionary = Nothing
End Sub

VBA need to run a macro when any cell in a certain range is updated through a formula

In my workbook on the sheet "Overview" I have the following calendar.
http://i.stack.imgur.com/PNRaU.jpg
You can select the check boxes to add a production day. The check boxes are tied to cells on the "Calculations" tab as seen below.
http://i.stack.imgur.com/H1nvr.jpg
The left chart is the output from the checkboxes, and the right chart gets the date from the overview tab if the corresponding chart value is True.
I then have been manually running the following VBA code to collect all the days from the right chart and paste them in a column
Private Sub CommandButton1_Click()
Dim Index As Long, V As Variant, ArrIn As Variant, ArrOut As Variant
Const TableRange As String = "S4:Y9"
Const OutputSheet As String = "Calculations"
Const OutputStartCell As String = "G2"
ArrIn = Range(TableRange)
ReDim ArrOut(1 To WorksheetFunction.CountA(Range(TableRange)), 1 To 1)
For Each V In ArrIn
If Len(V) Then
Index = Index + 1
ArrOut(Index, 1) = V
End If
Next
Worksheets(OutputSheet).Range(OutputStartCell).Resize(UBound(ArrOut)) = ArrOut
Range("G2:G12").Sort key1:=Range("G2:G12"), _
order1:=xlAscending, Header:=xlNo
End Sub
I would like this code to run automatically anytime a cell in the ("S4:Y9") range is updated. I have tried to use the change, and the calculate commands but have been unsuccessful. Any help would be appreciated.
EDIT:
I have added the following code, and nothing happens when I click the check boxes.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Target.Intersect(Range("S4:Y9")) Is Nothing Then 'The edited range must at least overlap with S4:Y9'
Dim Index As Long, V As Variant, ArrIn As Variant, ArrOut As Variant
Const TableRange As String = "S4:Y9"
Const OutputSheet As String = "Calculations"
Const OutputStartCell As String = "G2"
ArrIn = Range(TableRange)
ReDim ArrOut(1 To WorksheetFunction.CountA(Range(TableRange)), 1 To 1)
For Each V In ArrIn
If Len(V) Then
Index = Index + 1
ArrOut(Index, 1) = V
End If
Next
Worksheets(OutputSheet).Range(OutputStartCell).Resize(UBound(ArrOut)) = ArrOut
Range("G2:G12").Sort key1:=Range("G2:G12"), _
order1:=xlAscending, Header:=xlNo
End If
End Sub
EDIT 2:I am now attempting to assign the macro to all the checkboxes, so anytime a checkbox is changed it will run the macro. THe problem I am running into is, the code was originally written to be on the same worksheet as the calculations. I need to update it to reference the "Calculations" tab. I have tried to use "with" but it isnt working. See new code below.
Sub Macro1()
Dim Index As Long, V As Variant, ArrIn As Variant, ArrOut As Variant
With Worksheets("Calculations")
Const TableRange As String = "S4:Y9"
Const OutputSheet As String = "Calculations"
Const OutputStartCell As String = "G2"
ArrIn = Range(TableRange)
ReDim ArrOut(1 To WorksheetFunction.CountA(Range(TableRange)), 1 To 1) 'this line is highlighted when the error is displayed
For Each V In ArrIn
If Len(V) Then
Index = Index + 1
ArrOut(Index, 1) = V
End If
Next
Worksheets(OutputSheet).Range(OutputStartCell).Resize(UBound(ArrOut)) = ArrOut
Range("G2:G12").Sort key1:=Range("G2:G12"), _
order1:=xlAscending, Header:=xlNo
End With
End Sub
Sub Macro1()
Const TableRange As String = "S4:Y9"
Const OutputSheet As String = "Calculations"
Const OutputStartCell As String = "G2"
Dim Index As Long, V As Variant, ArrIn As Variant, ArrOut As Variant
Dim rngTbl as Range
With Worksheets(OutputSheet)
Set rngTbl = .Range(TableRange) '<< note dot!
End With
ArrIn = rngTbl.Value
ReDim ArrOut(1 To WorksheetFunction.CountA(rngTbl), 1 To 1)
For Each V In ArrIn
If Len(V) Then
Index = Index + 1
ArrOut(Index, 1) = V
End If
Next
Worksheets(OutputSheet).Range(OutputStartCell).Resize(UBound(ArrOut,1)) = ArrOut
Range("G2:G12").Sort key1:=Range("G2:G12"), _
order1:=xlAscending, Header:=xlNo
End Sub
Try the Worksheet Change event:
Private Sub Worksheet_Change(ByVal Target as Range)
If Not Target.Intersect(Range("S4:Y9")) is Nothing Then 'The edited range must at least overlap with S4:Y9'
'Your code here
End If
End Sub

Resources