Reputation: 107
I have the below code that will count certain strings based on date range and update the count in a cell.
Option Explicit
Const strFormTitle = "Enter Minimum and Maximum Dates in d/m/yyyy format" 'Edit for different regional date format
Const strShtName As String = "Latency" 'Name of worksheet with ranges to be processed
Const strDateFormat As String = "d mmm yyyy" 'Edit for different regional date format
Const strCrit1 As String = "Pass, Fail, In Progress" 'Criteria for output to AE2. (Can insert or delete criteria with comma between values. OK to have spaces with the commas)
Const strCrit2 As String = "COMPATIBLE" 'Criteria for column E. (One criteria only)
Const strDateRng As String = "K:K" 'Column with Dates
Const strCrit1Col As String = "O:O" 'Column with "Pass, Fail, In Progress"
Const strCrit2Col As String = "E:E" 'Column with "COMPATIBLE"
Const strOutput1 As String = "AE2" 'The cell for output "Pass, Fail, In Progress"
Const strOutput2 As String = "AF2" 'The cell for output "Pass, Fail, In Progress" plus "COMPATIBLE"
Private Sub UserForm_Initialize()
Me.lblTitle = strFormTitle
End Sub
Private Sub cmdProcess_Click()
Dim wf As WorksheetFunction
Dim ws As Worksheet
Dim rngDates As Range 'Range of dates
Dim rngCrit1 As Range 'Range to match Criteria 1
Dim rngCrit2 As Range 'Range to match Criteria 2
Dim dteMin As Date
Dim dteMax As Date
Dim rngOutput1 As Range
Dim rngOutput2 As Range
Dim arrSplit As Variant
Dim i As Long
Set wf = Application.WorksheetFunction
Set ws = Worksheets(strShtName)
With ws
Set rngDates = .Columns(strDateRng)
Set rngOutput1 = .Range(strOutput1)
Set rngOutput2 = .Range(strOutput2)
Set rngCrit1 = .Range(strCrit1Col)
Set rngCrit2 = .Range(strCrit2Col)
End With
dteMin = CDate(Me.txtMinDate)
dteMax = Int(CDate(Me.txtMaxDate) + 1)
If dteMin > dteMax Then
MsgBox "Minimum date must be less than maximum date." & vbCrLf & _
"Please re-enter a valid dates."
Exit Sub
End If
arrSplit = Split(strCrit1, ",")
'Following loop removes any additional leading or trailing spaces (Can be in the string constant)
For i = LBound(arrSplit) To UBound(arrSplit)
arrSplit(i) = Trim(arrSplit(i))
Next i
rngOutput1.ClearContents 'Start with blank cell
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput1.Value = rngOutput1.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i))
Next i
rngOutput2.ClearContents 'Start with blank cell
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput2.Value = rngOutput2.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i), rngCrit2, strCrit2)
Next i
End Sub
Private Sub cmdCancel_Click()
Unload Me
End Sub
Private Sub txtMinDate_AfterUpdate()
If IsDate(Me.txtMinDate) Then
Me.txtMinDate = Format(CDate(Me.txtMinDate), strDateFormat)
Else
MsgBox "Invalid Minimum date. Please re-enter a valid date."
End If
End Sub
Private Sub txtMaxDate_AfterUpdate()
If IsDate(Me.txtMaxDate) Then
Me.txtMaxDate = Format(CDate(Me.txtMaxDate), strDateFormat)
Else
MsgBox "Invalid Maximum date. Please re-enter a valid date."
End If
End Sub
Private Sub chkEntireRng_Click()
Dim wf As WorksheetFunction
Dim ws As Worksheet
Dim rngDates As Range
Set wf = WorksheetFunction
Set ws = Worksheets(strShtName)
With ws
Set rngDates = .Columns(strDateRng)
End With
If Me.chkEntireRng = True Then
Me.txtMinDate = Format(wf.Min(rngDates), strDateFormat)
Me.txtMaxDate = Format(wf.Max(rngDates), strDateFormat)
Me.txtMinDate.Enabled = False
Me.txtMaxDate.Enabled = False
Else
Me.txtMinDate = ""
Me.txtMaxDate = ""
Me.txtMinDate.Enabled = True
Me.txtMaxDate.Enabled = True
End If
End Sub
I'm not sure how to do the below task:
Upvotes: 1
Views: 72
Reputation: 1593
This code seems needlessly obfuscated with the excessive pointers, it may be good practice/learning to try refactoring it.
1: These lines are used to create the latency sheet object and the output range. I'd recommend doing the same for "Mysheet". Since you've not specified if the data is also in MySheet we need to assume it's still in the same place and not touch the existing references.
Const strShtName As String = "Latency" 'Name of worksheet with ranges to be processed
Dim ws As Worksheet
Set ws = Worksheets(strShtName)
Const strOutput1 As String = "AE2" 'The cell for output "Pass, Fail, In Progress"
Const strOutput2 As String = "AF2" 'The cell for output "Pass, Fail, In Progress" plus "COMPATIBLE"
Dim rngOutput1 As Range
Dim rngOutput2 As Range
With ws
Set rngOutput1 = .Range(strOutput1)
Set rngOutput2 = .Range(strOutput2)
End With
We'd add in the following to assign the new sheet object and paste range:
Dim wsMySheet As Worksheet
Set wsMySheet = Worksheets("MySheet")
Dim rngOutputMySheet as range
With wsMySheet
Set rngOutputMySheet = .range("CELLREFHERE")
End With
The paste itself happens at the end of the sub:
rngOutput1.ClearContents 'Start with blank cell
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput1.Value = rngOutput1.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i))
Next i
rngOutput2.ClearContents 'Start with blank cell
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput2.Value = rngOutput2.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i), rngCrit2, strCrit2)
Next i
You'd replace the range reference with your new one (rngOutputMySheet)
2: Criteria are set as follows:
Const strCrit1Col As String = "O:O" 'Column with "Pass, Fail, In Progress"
Const strCrit1 As String = "Pass, Fail, In Progress"
Dim rngCrit1 As Range 'Range to match Criteria 1
With ws
Set rngCrit1 = .Range(strCrit1Col)
End With
and used as follows:
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput2.Value = rngOutput2.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i), rngCrit2, strCrit2)
Next i
To add a new criteria we'd assign the criteria & range and add them into the criteria of the countifs formula:
Dim strCrit3 as String
strCrit3 = "Criteria list here"
Dim rngCrit3 as Range
With ws
set rngCrit3 = .Range("RANGEHERE")
End With
For i = LBound(arrSplit) To UBound(arrSplit)
rngOutput2.Value = rngOutput2.Value + wf.CountIfs(rngDates, ">=" & CLng(dteMin), _
rngDates, "<" & CLng(dteMax), _
rngCrit1, arrSplit(i), rngCrit2, strCrit2,rngCrit3, strCrit3)
Next i
Upvotes: 1