VBA: Looping through multiple rows of dates whilst interating dates inbetween - excel

Hi Stackoverflow troops,
My time has finally come to ask a question rather than rely on all the answers as I'm stuck!
I've got a dataset showing the input table on the left & the output table on the right. The first date range interates as I'd expect, however I can't get the next rows to iterate their date ranges or indeed the other cells to be populated. Step 1 is to get the dates to iterate.
https://i.stack.imgur.com/NzO4l.jpg
The code:
Sub GenerateDates()
Application.ScreenUpdating = False
Dim i, j As Integer
Dim finalRow, finalRow2, startRow As Long
finalRow = Range("A" & Rows.Count).End(xlUp).Row
finalRow2 = Range("M" & Rows.Count).End(xlUp).Row
For i = 2 To 4 'finalRow
finalRow2 = Range("M" & Rows.Count).End(xlUp).Row
startRow = finalRow2 + 1
Range("M" & startRow) = Range("A" & i)
j = i
Days = Int((Range("B" & i) - Range("A" & i)))
Do While j < 2 + Days
j = j + 1
Range("M" & j) = Range("M" & j - 1) + 1
Loop
Range("N" & startRow) = Range("C" & i)
Range("O" & startRow) = ""
Range("P" & startRow) = Range("F" & i)
Range("Q" & startRow) = Range("G" & i)
Next i
End Sub
Target output sample:
https://i.stack.imgur.com/LLaO2.jpg

Resolved the conundrum myself. :-)
Sub GenerateDates()
Application.ScreenUpdating = False
Dim i, j, numDays As Integer
Dim finalRow, finalRow2 As Long
Dim startDate, endDate As Date
Dim strType, strName, strCountry As String
finalRow = Range("A" & Rows.Count).End(xlUp).Row
finalRow2 = Range("M" & Rows.Count).End(xlUp).Row
If finalRow2 > 1 Then
Range("M2:Q" & finalRow2).Select
Selection.ClearContents
End If
Range("A1").Select
For i = 2 To finalRow
startDate = Range("A" & i).Value
endDate = Range("B" & i).Value
startDate = startDate - 1
strType = Range("C" & i).Value
numDays = Range("D" & i).Value
strName = Range("F" & i).Value
strCountry = Range("G" & i).Value
Do While startDate < endDate
finalRow2 = Range("M" & Rows.Count).End(xlUp).Row
finalRow2 = finalRow2 + 1
startDate = DateAdd("d", 1, startDate)
Range("M" & finalRow2).Value = startDate
Range("N" & finalRow2).Value = strType
'Function to split decimal numDays
Range("P" & finalRow2).Value = strName
Range("Q" & finalRow2).Value = strCountry
Loop
Next i
Application.ScreenUpdating = True
End Sub

Related

How to convert several non-adjacent columns to lowercase

This is looping through a worksheet that is about 10k rows and it is taking a considerable amount of time. Is there a way to do this faster aside from an array? thank you
For i = 2 To spberowcnt
With spbe30
.Range("b" & i).Value = LCase(.Range("b" & i).Value)
.Range("d" & i).Value = LCase(.Range("d" & i).Value)
.Range("i" & i).Value = LCase(.Range("i" & i).Value)
.Range("j" & i).Value = LCase(.Range("j" & i).Value)
.Range("l" & i).Value = LCase(.Range("l" & i).Value)
.Range("m" & i).Value = LCase(.Range("m" & i).Value)
.Range("n" & i).Value = LCase(.Range("n" & i).Value)
.Range("p" & i).Value = LCase(.Range("p" & i).Value)
.Range("q" & i).Value = LCase(.Range("q" & i).Value)
.Range("r" & i).Value = LCase(.Range("r" & i).Value)
.Range("z" & i).Value = LCase(.Range("z" & i).Value)
.Range("aa" & i).Value = LCase(.Range("aa" & i).Value)
End With
Next i
For i = 2 To spberowcnt
With spbe60
.Range("b" & i).Value = LCase(.Range("b" & i).Value)
.Range("d" & i).Value = LCase(.Range("d" & i).Value)
.Range("i" & i).Value = LCase(.Range("i" & i).Value)
.Range("j" & i).Value = LCase(.Range("j" & i).Value)
.Range("l" & i).Value = LCase(.Range("l" & i).Value)
.Range("m" & i).Value = LCase(.Range("m" & i).Value)
.Range("n" & i).Value = LCase(.Range("n" & i).Value)
.Range("p" & i).Value = LCase(.Range("p" & i).Value)
.Range("q" & i).Value = LCase(.Range("q" & i).Value)
.Range("r" & i).Value = LCase(.Range("r" & i).Value)
.Range("z" & i).Value = LCase(.Range("z" & i).Value)
.Range("aa" & i).Value = LCase(.Range("aa" & i).Value)
End With
Next i
This is the array solution
Sub test()
Application.ScreenUpdating = False
Dim arrWorksheets(1) As Variant, ws As Worksheet
Set arrWorksheets(0) = spbe30
Set arrWorksheets(1) = spbe60
Dim arrColumns As Variant
arrColumns = Array("B", "D", "AA") 'adjust to your needs
Dim arrValues As Variant
Dim iWs As Long, iC As Long, i As Long
For iWs = 0 To UBound(arrWorksheets)
Set ws = arrWorksheets(iWs)
For iC = 0 To UBound(arrColumns)
arrValues = ws.Range(arrColumns(iC) & "2").Resize(spberowcnt).Value
For i = 1 To UBound(arrValues, 1)
arrValues(i, 1) = LCase(arrValues(i, 1))
Next
ws.Range(arrColumns(iC) & "2").Resize(spberowcnt).Value = arrValues
Next
Next
Application.ScreenUpdating = True
End Sub
Alternative: helper columns ...
You could try something like the following, looping over the columns instead of the individual cells and using Evaluate and Lower on the entire column. You could also process adjacent columns together.
cols = Array("B", "D", "I:J", "L:N", "P:R", "Z:AA")
For i = LBound(cols) to Ubound(cols)
Dim col As String
col = cols(i)
With spbe30
Dim rng As Range
Set rng = .Rows("2:" & spberowcnt).Columns(col)
rng.Value = .Evaluate("LOWER(" & rng.Address & ")")
End With
Next
But as mentioned in comments, an array is probably the way to go.

For loop with if/and/or statement

I have the following code:
Sub CreateDisableLists()
Dim LastRow As Long
Dim i As Long
LastRow = Cells(Rows.Count, "J").End(xlUp).Row
For i = 2 To LastRow
If _
Range("G" & i).Value = "DSDFDFFD" And Range("I" & i).Value = "Enabled" Or _
Range("G" & i).Value = "SFDDS" And Range("I" & i).Value = "Enabled" Or _
Range("G" & i).Value = "FFDFDSSF" And Range("I" & i).Value = "Enabled" Or _
Range("G" & i).Value = "FDFDSVSFD" And Range("I" & i).Value = "Enabled" Or _
Range("G" & i).Value = "FDFDSFD" And Range("I" & i).Value = "Enabled" Or _
Range("G" & i).Value = "GHFH" And Range("I" & i).Value = "Enabled" _
Then
Range("K" & i).Value = "TRUE"
Else
Range("K" & i).Value = "FALSE"
End If
Next i
End Sub
How can I compress the lines between "If" and "Then" so that I loop through a list of (DSDFDFFD, SFDDS, FFDFDSSF, etc") instead of what is written above? Using this code I need to add 68 lines between "If" and "Then".
You could start by setting K to be FALSE, then using If on column I, and Select Case on column G:
Sub sCreateDisableLists()
Dim LastRow As Long
Dim i As Long
LastRow = Cells(Rows.Count, "J").End(xlUp).Row
For i = 2 To LastRow
Range("K" & i).Value = "FALSE"
If Range("I" & i).Value = "Enabled" Then
Select Case Range("G" & i).Value
Case "xxx1", "xxx2", "xxx3", "xxx4", "xxx5", "xxx6"
Range("K" & i).Value = "TRUE"
End Select
End If
Next i
End Sub
If using multiple Or/And statements I highly recommend to use parenthesis to group them as you want them to validate, or you might not get the result you expect.
Your If statement could be like:
Dim Arr() As Variant
Arr = Array("DSDFDFFD", "SFDDS", "FFDFDSSF") 'define your keys here
If Range("I" & i).Value = "Enabled" And IsInArray(Range("I" & i).Value, Arr) Then
Range("K" & i).Value = "TRUE"
Else
Range("K" & i).Value = "FALSE"
End If
or even less:
Dim Arr() As Variant
Arr = Array("DSDFDFFD", "SFDDS", "FFDFDSSF") 'define your keys here
Range("K" & i).Value = UCase(Range("I" & i).Value = "Enabled" And IsInArray(Range("I" & i).Value, Arr))
using this function
Public Function IsInArray(ByVal stringToBeFound As String, ByVal Arr As Variant) As Boolean
IsInArray = (UBound(Filter(Arr, stringToBeFound)) > -1)
End Function
You could try:
Option Explicit
Sub CreateDisableLists()
Dim LastRow As Long, i As Long, y As Long
Dim strValues As String: strValues = "DSDFDFFD,SFDDS,FFDFDSSF,FDFDSVSFD,FDFDSFD,GHFH"
Dim strIvalue As String: strIvalue = "Enabled"
Dim arr As Variant
Dim BooleanStatus As Boolean
With ThisWorkbook.Worksheets("Sheet1")
LastRow = .Cells(.Rows.Count, "J").End(xlUp).Row
arr = Split(strValues, ",")
For i = 2 To LastRow
BooleanStatus = False
For y = LBound(arr) To UBound(arr)
If (.Range("G" & i).Value = arr(y)) And .Range("I" & i).Value = strIvalue Then
BooleanStatus = True
Exit For
End If
Next y
If BooleanStatus = True Then
.Range("K" & i).Value = "TRUE"
Else
.Range("K" & i).Value = "FALSE"
End If
Next i
End With
End Sub
Not very much to be improved, but the next code would be a little more compact:
Sub testImproveCode()
Dim LastRow As Long, i As Long
Dim j As Long, boolOk As Boolean
LastRow = Cells(Rows.count, "J").End(xlUp).Row
For i = 2 To LastRow
For j = 1 To 6
If Range("G" & i).value = "xxx" & j And _
Range("I" & i).value = "Enable" Then
boolOk = True: Exit For
Next j
If boolOk Then
Range("K" & i).value = "TRUE": boolOk = False
Else
Range("K" & i).value = "FALSE"
End If
Next i
End Sub

IF logic for all date variables empty

I'm trying to modify the below function to include logic where if the variables PPD_1_Date, PPD_2_Date and TSpot_Date are all empty (blank) then output to my "Error" worksheet.
I have rows that should fall under this logic, however they are falling under the Else condition instead.
Function PPDdate()
Dim PPD_1_Date As Date
Dim PPD_2_Date As Date
Dim TSpot_Date As Variant
Dim i As Long, j As Long, k As Long
j = Worksheets("PPDCI").Range("A" & Rows.Count).End(xlUp).Row + 1
k = Worksheets("Error").Range("A" & Rows.Count).End(xlUp).Row + 1
For i = 2 To lstrow
PPD_1_Date = Worksheets("Data").Range("AW" & i)
PPD_2_Date = Worksheets("Data").Range("BA" & i)
Entity = Worksheets("Data").Range("J" & i)
Dept = Worksheets("Data").Range("M" & i)
TSpot_Date = Worksheets("Data").Range("AS" & i)
If PPD_1_Date > PPD_2_Date Then
Worksheets("PPDCI").Range("A" & j & ":C" & j).Value = Worksheets("Data").Range("A" & i & ":C" & i).Value
Worksheets("PPDCI").Range("F" & j).Value = PPD_1_Date
Worksheets("PPDCI").Range("G" & j).Value = Worksheets("Data").Range("AX" & i).Value
Worksheets("PPDCI").Range("H" & j).Value = Worksheets("Data").Range("AZ" & i).Value
Worksheets("PPDCI").Range("I" & j).Value = Worksheets("Data").Range("AY" & i).Value
j = j + 1
Else
If PPD_1_Date < PPD_2_Date Then
Worksheets("PPDCI").Range("A" & j & ":C" & j).Value = Worksheets("Data").Range("A" & i & ":C" & i).Value
Worksheets("PPDCI").Range("F" & j).Value = PPD_2_Date
'Worksheets("PPDCI").Range("G" & j).Value = "ELSE IF CONDITION"
Worksheets("PPDCI").Range("G" & j).Value = Worksheets("Data").Range("BB" & i).Value
Worksheets("PPDCI").Range("H" & j).Value = Worksheets("Data").Range("BD" & i).Value
Worksheets("PPDCI").Range("I" & j).Value = Worksheets("Data").Range("BC" & i).Value
j = j + 1
Else
'If IsEmpty(Worksheets("Data").Range(PPD_1_Date & i).Value) = True And IsEmpty(Worksheets("Data").Range(PPD_2_Date & i).Value) = True Then
'GoTo EmptyRange
'Else
If (InStr(1, Entity, "CNG Hospital") Or InStr(1, Entity, "Home Health") Or InStr(1, Entity, "Hospice") Or InStr(1, Dept, "Volunteers") Or ((IsEmpty(PPD_1_Date) = True) And (IsEmpty(PPD_2_Date) = True))) And IsEmpty(TSpot_Date) = True Then
Worksheets("Error").Range("A" & k & ":H" & k).Value = Worksheets("Data").Range("A" & i & ":C" & i).Value
Worksheets("Error").Range("F" & k).Value = "REVIEW PPD DATA"
k = k + 1
Else
Worksheets("PPDCI").Range("A" & j & ":C" & j).Value = Worksheets("Data").Range("A" & i & ":C" & i).Value
Worksheets("PPDCI").Range("F" & j).Value = TSpot_Date
Worksheets("PPDCI").Range("G" & j).Value = Worksheets("Data").Range("AX" & i).Value
Worksheets("PPDCI").Range("H" & j).Value = Worksheets("Data").Range("AY" & i).Value
Worksheets("PPDCI").Range("I" & j).Value = "NO PPD DATES BUT HAS TSPOT DATE1"
j = j + 1
End If
End If
End If
'EmptyRange:
'k = k + 1
Next i
End Function
Here is the code I added to the other OR logic;
Or ((IsEmpty(PPD_1_Date) = True) And (IsEmpty(PPD_2_Date) = True))
Example row has empty cells in columns AW, BA, and AS, so it should write to my Error worksheet. Is there a syntax or logic issue? I did initially have TSPOT_Date defined as a Date variable, however I was getting a '1004' runtime error (I think because some column rows are empty) so I changed to Variant, however logic still doesn't work as I expect.
The problem you're running into is that you can't check if Date variables are "empty" using isEmpty() or even with Len() because the default value for a date is 30-Dec-1899 00:00:00, so there is always a value in a Date variable.
Instead, you should check to see that a Date variable is empty/has not been filled like this
If PPD_2_Date = 0 Then
...

How do I set column range as variable in Excel using a macro?

I have a matrix of dates with each one representing a different task, project, planned/actual start/finish. Please see the attached picture:
Screenshot of data
I have left everything in general terms as the actual names are confidential to my company. Anyhow, every row and column tells a different piece of information. Every two rows tells what project a date belongs to, and every row tell whether that date is a start or finish date. Every two columns tells what task the date belongs to, and every column states whether the date is a predicted date or an actual one.
Setting aside, what I am trying to do with this data is create a macro that will search all of these dates within a range set by the user, and list each date with the aforementioned information. So far, I have a code does this for individual rows:
Sub Sort_By_Date()
Dim StartDate As Date
Dim EndDate As Date
Dim i As Integer
StartDate = Range("F61").Value
EndDate = Range("G61").Value
'Clears out cells
Range("Z74:AD200").Value = ""
m = 74
For i = 71 To 200
If Range("C" & i).Value >= StartDate And Range("C" & i).Value <= EndDate Then
Range("Z" & m).Value = Range("C" & i).Value
Range("AA" & m).Value = Range("A" & i).Value
Range("AB" & m).Value = Range("C69")
Range("AC" & m).Value = Range("B" & i)
Range("AD" & m).Value = Range("C70")
m = m + 1
End If
If Range("D" & i).Value >= StartDate And Range("D" & i).Value <= EndDate Then
Range("Z" & m).Value = Range("D" & i).Value
Range("AA" & m).Value = Range("A" & i).Value
Range("AB" & m).Value = Range("D69")
Range("AC" & m).Value = Range("B" & i)
Range("AD" & m).Value = Range("D70")
m = m + 1
End If
If Range("E" & i).Value >= StartDate And Range("E" & i).Value <= EndDate Then
Range("Z" & m).Value = Range("E" & i).Value
Range("AA" & m).Value = Range("A" & i).Value
Range("AB" & m).Value = Range("E69")
Range("AC" & m).Value = Range("B" & i)
Range("AD" & m).Value = Range("E70")
m = m + 1
End If
...
...
...
Hopefully you can see the pattern here.
I can keep copying and pasting that If Then statement for every column, but there has to be a more efficient way to do it. I am fairly new to Excel macros (someone else actually wrote the basis of that code), so I am not sure how to make it do what I want it to. I imagine it will involve making the column letters into numbers, and then into variables, but I just don't know. I have tried looking it up, but I am having trouble applying what I found to my specific application.
So my main question: How do I get that If Then statement to repeat for every column of data I have without having to copy and paste it a million times?
Seems like you simply need a nested for loop:
Dim StartDate As Date
Dim EndDate As Date
Dim i As Integer, c as integer
StartDate = Range("F61").Value
EndDate = Range("G61").Value
'Clears out cells
Range("Z74:AD200").Value = ""
m = 74
'From column C to column AD
For c = 3 TO 30
For i = 71 To 200
If Range("C" & i).Value >= StartDate And Range("C" & i).Value <= EndDate Then
Range("Z" & m).Value = cells(i,c).Value
Range("AA" & m).Value = Range("A" & i).Value
Range("AB" & m).Value = cells(69,c).value
Range("AC" & m).Value = Range("B" & i)
Range("AD" & m).Value = cells(70,c).value
m = m + 1
End If
Next
Next
Turns out what I was looking for was this:
Sub Sort_By_Date()
Dim StartDate As Date
Dim EndDate As Date
Dim rwMin As Integer
Dim colMin As Integer
Dim rwMax As Integer
Dim colMax As Integer
Dim rwIndex As Integer
Dim colIndex As Integer
StartDate = Range("F61").Value
EndDate = Range("G61").Value
rwMin = 4
colMin = 3
rwMax = 37
colMax = 68
Range("L44:R2600").Value = ""
m = 44
For colIndex = colMin To colMax
For rwIndex = rwMin To rwMax
If Cells(rwIndex, colIndex).Value >= StartDate And Cells(rwIndex, colIndex).Value <= EndDate Then
Range("L" & m).Value = Cells(rwIndex, colIndex).Value
Range("M" & m).Value = Cells(rwIndex, 1).Value
Range("N" & m).Value = Cells(2, colIndex).Value
Range("O" & m).Value = Cells(1, colIndex).Value
Range("P" & m).Value = Cells(rwIndex, 2).Value
Range("Q" & m).Value = Cells(3, colIndex).Value
m = m + 1
End If
Next rwIndex
Next colIndex
End Sub
Basically, I needed to switch from using Range.Value to Cells.Value and then uses indexes. However, I seem to now have a separate issue of causing a runtime error with another function that I want to include, but that's a topic for a different post. It works right now without that function, though. Thanks for your input!

Check duplicates copy in one cell

I have this code to check duplicates, If it find duplicates (or more) in cell L, is it possible to copy the values from cells in the K column into ONE cell?
Sub check_duplicates()
Dim x As Long
Dim LastRow As Long
Dim rng As String
LastRow = Range("L65536").End(xlUp).Row
For x = LastRow To 1 Step -1
If Application.WorksheetFunction.CountIf(Range("L2:L" & x), Range("L" & x).Value) > 1 Then
Range("L" & x).Copy
End If
Next x
End Sub
I hope is what you want to do, let me know.
Sub Test()
Dim lastrow As Long
lastrow = Range("L" & Rows.Count).End(xlUp).Row
For i = 2 To lastrow
lastrow = Range("L" & Rows.Count).End(xlUp).Row
For j = i + 1 To lastrow
If Range("L" & j).Value = Range("L" & i).Value Then
If Not IsEmpty(Range("K" & i)) Then
Range("K" & i) = Range("K" & i) & "," & " " & Range("L" & j)
Rows(j).EntireRow.Delete
Else
Range("K" & i) = Range("L" & j)
End If
j = j - 1
End If
Next j
Next i
End Sub

Resources