There are many areas of improvement in this coding.
A most important part of code hygiene is proper indenting, and always use Option Explicit
.
Also, name your variables to something meaningful. For example, you use StartDate
, but it is not a date (which the name implies), but a range.
You comment that your first function returns an Array
, but it actually returns a Collection
. Array
s are ordered, Collection
s are not, particularly in VBA.
You have some Boolean
anti-patterns happening:
If TheDay = Day(currentDate) And TheUnit = Unit Then
Test1 = True
Else: Test1 = False ' No need to run things onto a single line, especially if this is inconsistent with the other code.
End If
Can simply be:
Test1 = (TheDay = Day(currentDate)) And (TheUnit = Unit ) ' perhaps "FindMatch" is better descriptive name.
You set AdvRow
and PropRow
(what are these anyway - proper naming?) relative to each other within the loop, but you don't change either in that loop - so a single variable (AdvRow
) will suffice.
You don't error check to ensure that the data you are reading is the right form - what happens if the data sheet does not exist, or that cell that is read is not a date?
You use NextRow
while in a loop - but you already access a cell in the loop that tells you what the row is. This is one variable that can be dropped. And you are using NextRow
as the CurrentRow
- this is another example of a confusing variable name.
A big performance hit will come from having three nested loops, but also accessing each cell individually within those loops. Each time you make the program switch from looking at the VBA to looking at the Excel ranges is a cost in performance - this is why taking a range and putting it into an array improves efficiency.
'Function to return Collection of dates between Start Date and End Date
'**** You don't check to see if Start comes before End - what does it mean if they are the wrong way round?
Function GetDatesRange(dateStart As Date, dateEnd As Date) As Collection
Dim dates As New Collection
Dim currentDate As Date
currentDate = dateStart
Do While currentDate <= dateEnd
dates.Add currentDate
currentDate = DateAdd("d", 1, currentDate)
Loop
Set GetDatesRange = dates
End Function
'Sub to move raw data into predictable format
Sub Program()
Dim rawData As Variant
Dim currentRow As Long
Application.ScreenUpdating = False
With Sheets("Raw")
rawData = .Range(Union(.Range("E2:E1000"), .Range("G2:G1000"), .Range("C2:C1000"), .Range("B2:B1000"), .Range("L2:L1000"))).Value
End With
'will be a more efficient way of setting the array, but this will do for now
' 0..998, 0..4 array - datestart, dateend, facility, unit, pax
'Removes the following code:
'Set StartDate = Range("E2:E1000")
'NextRow = 2
'Sheets("Raw").Activate
'For Each Cell In StartDate
'Set dateStartCell = Range("E" & NextRow)
'Set dateEndCell = Range("G" & NextRow)
For currentRow = LBound(rawData, 1) To UBound(rawData, 1)
Dim allDates As Collection
Dim currentDateSter As Variant
'Set allDates = GetDatesRange(dateStartCell.Value, dateEndCell.Value)
Set allDates = GetDatesRange(CDate(rawData(currentRow, 0)), CDate(rawData(currentRow, 0)))
'Following code is no longer necessary
' Facility = Cells(NextRow, 3)
' Unit = Cells(NextRow, 2)
' Pax = Cells(NextRow, 12)
'Evaluating if the date and name already exist
For Each currentDateSter In allDates
Dim checkSheet As Worksheet ' not sure what to call this
Dim currentDate As Date
Dim advRow As Long
currentDate = CDate(currentDateSter) ' what if this is not a date?
Set checkSheet = Sheets(MonthName(Month(currentDate), True) & Year(currentDate))
advRow = 3
'evaluating if the date and name already exists if it does, and determines row for data entry
'eventually end up writing over data if it already exists however, column C has 125 unique possibilities
'that will fill another column in the month tabs
Do
Dim isMatch As Boolean ' Test1
Dim isOffsetMatch As Boolean ' Test2
Dim theDay As String
Dim theUnit As String
advRow = advRow + 1
'PropRow = AdvRow
theDay = checkSheet.Cells(advRow, 1) ' fully qualified access to cells - no ambiguity
theUnit = checkSheet.Cells(advRow, 2)
isMatch = (theDay = Day(currentDate)) And (theUnit = rawData(currentRow, 3))
isOffsetMatch = (theDay = theUnit)
Loop Until isMatch Or isOffsetMatch
checkSheet.Cells(advRow, 2).Value = rawData(currentRow, 3)
checkSheet.Cells(advRow, 1).Value = Day(currentDate)
checkSheet.Cells(advRow, 3).Value = rawData(currentRow, 4)
Next currentDateSter
Next currentRow
Application.ScreenUpdating = True
End Sub
Removing all my additional comments in Program
gives you:
'Sub to move raw data into predictable format
Sub Program()
Dim rawData As Variant
Dim currentRow As Long
Application.ScreenUpdating = False
With Sheets("Raw")
rawData = .Range(Union(.Range("E2:E1000"), .Range("G2:G1000"), .Range("C2:C1000"), .Range("B2:B1000"), .Range("L2:L1000"))).Value
End With
For currentRow = LBound(rawData, 1) To UBound(rawData, 1)
Dim allDates As Collection
Dim currentDateSter As Variant
Set allDates = GetDatesRange(CDate(rawData(currentRow, 0)), CDate(rawData(currentRow, 0)))
'Evaluating if the date and name already exist
For Each currentDateSter In allDates
Dim checkSheet As Worksheet ' not sure what to call this
Dim currentDate As Date
Dim advRow As Long
currentDate = CDate(currentDateSter) ' what if this is not a date?
Set checkSheet = Sheets(MonthName(Month(currentDate), True) & Year(currentDate))
advRow = 3
'evaluating if the date and name already exists if it does, and determines row for data entry
'eventually end up writing over data if it already exists however, column C has 125 unique possibilities
'that will fill another column in the month tabs
Do
Dim isMatch As Boolean ' Test1
Dim isOffsetMatch As Boolean ' Test2
Dim theDay As String
Dim theUnit As String
advRow = advRow + 1
theDay = checkSheet.Cells(advRow, 1) ' fully qualified access to cells - no ambiguity
theUnit = checkSheet.Cells(advRow, 2)
isMatch = (theDay = Day(currentDate)) And (theUnit = rawData(currentRow, 3))
isOffsetMatch = (theDay = theUnit)
Loop Until isMatch Or isOffsetMatch
checkSheet.Cells(advRow, 2).Value = rawData(currentRow, 3)
checkSheet.Cells(advRow, 1).Value = Day(currentDate)
checkSheet.Cells(advRow, 3).Value = rawData(currentRow, 4)
Next currentDateSter
Next currentRow
Application.ScreenUpdating = True
End Sub
Of course, there may be some other logic paths, or even, perhaps, using Excel native functions that could help refine the problem.
I haven't been able to test the code (naturally), but it does compile in VBA.
Raw Data
and a couple of matching entries on one of the monthly tabs. \$\endgroup\$column b
hold aorganization
,column E
holds astart date
andcolumn G
holds anend date
. for eachorganization
inb
I am breaking it up from thestart date
to theend date
1 instance oforganization
for each day. Theif statements
I will add in, will put facilities arrayed across the rest of themonth tab
trackers. \$\endgroup\$