VBA infinite nested for loop - excel

I'm trying to write nested for loops to loop through the rows of a column to do some calculation then go to the next column to do it again. The logic makes sense to me, but the output to the sheet flashes back and forth between the correct answers and overwriting everything with the same number, and it just keeps doing that. Please let me know if I need to clarify on anything, thanks in advance.
Sub findAvg2()
Dim maxVal As Double
Dim preHr As Double
Dim nextHr As Double
Dim cVal As Double
Dim pVal As Double
Dim nVal As Double
Dim avg As Double
Dim maxAvg As Double
Dim i As Integer 'row
Dim j As Integer 'col
Dim lRow As Integer
Dim lCol As Integer
lRow = Cells(Rows.count, 1).End(xlUp).row 'Find the number of rows in column A(1)
lCol = Cells(1, Columns.count).End(xlToLeft).Column
For i = 19 To lRow
For j = 2 To lCol
maxVal = Cells(2, j).Value
preHr = Cells(8, j).Value
nextHr = Cells(9, j).Value
avg = (maxVal + preHr + nextHr) / 3
If Cells(i, j).Value > 0 Then
pVal = Cells(i - 1, j).Value
cVal = Cells(i, j).Value
nVal = Cells(i + 1, j).Value
maxAvg = (pVal + cVal + nVal) / 3
If avg > maxAvg Then
maxAvg = avg
End If
End If
Cells(12, j).Value = maxAvg
'Debug.Print maxAvg
Next j
Next i
End Sub

I reviewed your code and find nothing wrong with it. The modifications I did make appear to me to be of cosmetic nature. Here is the result.
Sub findAvg2()
' 005
Dim maxVal As Double
Dim preHr As Double
Dim nextHr As Double
Dim cVal As Double
Dim pVal As Double
Dim nVal As Double
Dim Avg As Double
Dim maxAvg As Double
Dim Cl As Long ' last used column
Dim Rl As Long ' last used row
Dim C As Long ' column
Dim R As Long ' row
' Find the number of used columns and roaws in the sheet
Cl = Cells(1, Columns.Count).End(xlToLeft).Column
Rl = Cells(Rows.Count, 1).End(xlUp).Row
For R = 19 To Rl
For C = 2 To Cl
maxVal = Cells(2, C).Value
preHr = Cells(8, C).Value
nextHr = Cells(9, C).Value
maxAvg = (maxVal + preHr + nextHr) / 3
cVal = Cells(R, C).Value
If cVal > 0 Then
pVal = Cells(R - 1, C).Value
nVal = Cells(R + 1, C).Value
Avg = (pVal + cVal + nVal) / 3
If Avg > maxAvg Then maxAvg = Avg
End If
Cells(12, C).Value = maxAvg
'Debug.Print maxAvg
Next C
Next R
End Sub
There is a possible weakness in this line of your code. For R = 19 To Rl. Since you are including the previous row in your calculation of averages row 18 must contain data. If it doesn't, and you can't exclude the first data row from evaluation, special provision must be made for the calculation of the initial maxAvg.
All action takes place on the ActiveSheet. This is an arrangement I instinctively dislike. Unless you are calling the sub from a button on that sheet - and even then, in case a smart alec wants to use F5 instead - I would name the sheet in the code. Use a CodeName both for greater security and to allow users the freedom to rename the sheet. This code will run on whatever sheet that happens to be active. It doesn't even have to be in the same workbook.

Related

There is Not Enough Memory To Complete this Action

I've written the below code to modify a speadsheet that has tens of thousands of lines. Whenever I run the code, it burns through the lines fast enough, will complete about 10k lines in 3-4 minutes or so. But every time I run it, it gets to about line 25K or so, and crashes, telling me I don't have enough memory, and will suggest upgrading to 64-bit. I have a macro that created the sheet without incident, and it's much more complex, so seems odd this code crashes it. Anything in this code that you'd think would cause my issue? Or is 64-bit likely the right fix?
Sub TPOUploadCADUplicate()
'This takes the TPO Mass upload sheet and duplicates it below for Canada. Unlike above, it doesn't do anything to the US part on top
Dim Answer As String
Dim BigMarkup As Double
Dim CAPrice As Double
Dim Cost As Double
Dim i As Long
Dim rn As Long
Dim rn2 As Long
Dim SKUCount As Double
Dim STMarkup As Double
Dim USPrice As Double
Dim lr As Long
Dim DescLen As Integer
With Application
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
'Make sure you didn't accidentally leave the description length column in
If Cells(1, 3) <> "VENDOR # (9 SPACES)" Then
DescLen = MsgBox("Yo, bro. I think you left the description length column in. You want to delete that shit? I can't proceed otherwise.", vbYesNo)
If DescLen = 6 Then
Columns(3).Delete
ElseIf DescLen = 7 Then
Exit Sub
End If
End If
Columns(6).NumberFormat = "#.00"
'Loop through each one, doing the math from the TPO price calculator Connie has
If Cells(2, 1) = "" Then Exit Sub
rn = Cells(1, 1).End(xlDown).Row
rn2 = rn + 1
rn = 2
SKUCount = rn2 - rn
For i = 1 To SKUCount
Application.StatusBar = "Progress: " & i & " of " & SKUCount & " - " & Format(i / SKUCount, "0%")
Rows(rn2).Value = Rows(rn).Value
USPrice = Cells(rn, 4)
If USPrice * CAMarkup < 20 Then
CAPrice = Round((USPrice) * CAMarkup, 1) + 0.09
Else
CAPrice = WorksheetFunction.RoundDown((USPrice) * CAMarkup, 0) + 0.99
End If
Cells(rn2, 4) = CAPrice
Cells(rn2, 6).Value = Cells(rn2, 6).Value * CAMarkup
Cells(rn2, 22) = "CAM"
rn = rn + 1
rn2 = rn2 + 1
Next i
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
.StatusBar = False
End With
End Sub
Might be better (faster) to read all the data to an array, then work on the array, before putting it on the sheet after the existing data.
Sub TPOUploadCADUplicate()
Dim ans
Dim CAPrice As Double
Dim SKUCount As Double
Dim STMarkup As Double, CAMarkup As Double
Dim USPrice As Double
Dim DescLen As Integer, ws As Worksheet, arr, lr As Long, lc As Long, r As Long
Set ws = ActiveSheet 'best to be explicit about which sheet you're working with
'Make sure you didn't accidentally leave the description length column in
If ws.Cells(1, 3) <> "VENDOR # (9 SPACES)" Then
ans = MsgBox("Yo, bro. I think you left the description length column in. " & _
"You want to delete that shit? I can't proceed otherwise.", vbYesNo)
If ans <> vbYes Then Exit Sub
ws.Columns(3).Delete
End If
ws.Columns(6).NumberFormat = "#.00"
lr = ws.Cells(Rows.Count, "A").End(xlUp).Row 'last row
If lr = 1 Then Exit Sub 'no data?
lc = ws.Cells(1, Columns.Count).End(xlToLeft).Column 'last column
CAMarkup = 1.1 '<< for example
arr = ws.Range("A2", ws.Cells(lr, lc)).value 'copy the existing data as an array
For r = 1 To UBound(arr, 1) 'loop over the array and make adjustments
USPrice = arr(r, 4)
If USPrice * CAMarkup < 20 Then
CAPrice = Round((USPrice) * CAMarkup, 1) + 0.09
Else
CAPrice = WorksheetFunction.RoundDown((USPrice) * CAMarkup, 0) + 0.99
End If
arr(r, 4) = CAPrice
arr(r, 6) = arr(r, 6) * CAMarkup
arr(r, 22) = "CAM"
Next r
'put the data on the sheet
ws.Cells(lr + 1, "A").Resize(UBound(arr, 1), UBound(arr, 2)).value = arr
End Sub

VBA How do I adjust my code to speed up the loop process?

I have a worksheet change event that, when 3 adjacent cells are filled in columns C,D and E it is documented in a different sheet with the date as well as the sheet that where the cells have been filled in.
Then every occurrence of date data is summed up and plotted on a calendar essentially showing how many entries have occurred for everyday of the year.
The issue is, is that the code loops through all days of the year which makes it incredibly slow, is there any way to make adjustments so it doesn't loop through everything or at least to speed up the process?
This is the code for the loops in question:
With Sheets("Log")
Set dfCell = dws.Cells(dws.Rows.Count, dCol) _
.End(xlUp).Offset(1)
dfCell.Value = Format(Date, "mm/dd/yyyy")
dfCell.Offset(, 1).Value = ActiveSheet.Name
dfCell.Offset(, 2).Value = srAddress
Dim arrDates As Range
Dim LastRow As Long
Dim DateRange As Long
Dim RowCount As Long
Dim ClmnAmnt As Long
Dim ClmnDate() As Variant
Dim AddrArr() As Variant
Dim ClmnNmbr As Long
Dim shtNames As Range
Dim TypCount As Long
Dim FrstLetter() As Variant
Dim SheetIdent As String
Dim lastAddrs As String
For RowCount = 1 To 60
Select Case RowCount
Case 2, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52, 57
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
DateRange = WorksheetFunction.CountA(.Range("F" & RowCount & ":AJ" & RowCount))
For TypCount = 1 To 3
SheetIdent = .Cells(RowCount + TypCount, 5).Value
For ClmnNmbr = 1 To DateRange
ReDim AddrArr(DateRange)
AddrArr(ClmnNmbr) = .Cells(RowCount, ClmnNmbr + 5).Value
Set arrDates = .Range("A60:A" & LastRow)
Set shtNames = .Range("B60:B" & LastRow)
ReDim FrstLetter(DateRange)
FrstLetter(ClmnNmbr) = Application.CountIfs(arrDates, AddrArr(ClmnNmbr), shtNames, SheetIdent)
Worksheets("Log").Cells(TypCount + RowCount, ClmnNmbr + 5).Value = Application.Transpose(FrstLetter(ClmnNmbr))
Next ClmnNmbr
Next TypCount
Case Else
End Select
Next RowCount
End With
Treat each month as a separate array of 3 rows by 28/29/30/31 columns then you can read, update counts and write back.
Option Explicit
Sub UpdateCounts()
Const COL_LETTER = "E"
Dim rngA As Range, rngB As Range, counts, t0 As Single: t0 = Timer
Dim LastRow As Long, LastCol As Long, dates, letters
Dim i As Long, j As Long, d As Long, m As Long, r As Long
With Sheets("Log")
' fill data
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
Set rngA = .Range("A60:A" & LastRow)
Set rngB = .Range("B60:B" & LastRow)
' scan down sheet rows 2 to 57
For m = 1 To 12 ' jan to dec
r = 2 + (m - 1) * 5
LastCol = .Cells(r, "AK").End(xlToLeft).Column
d = LastCol - 5 ' no of days
If d >= 28 And d <= 31 Then
dates = .Cells(r, "F").Resize(, d).Value
letters = .Cells(r + 1, "E").Resize(3).Value
counts = .Cells(r + 1, "F").Resize(3, d).Value
For j = 1 To d ' days
For i = 1 To 3
' update counts
counts(i, j) = Application.CountIfs(rngA, dates(1, j), rngB, letters(i, 1))
Next
' update table
.Cells(r + 1, "F").Resize(3, d) = counts
Next
Else
'Debug.Print r, "No table for month " & m
End If
Next
End With
MsgBox "Done in " & Format(Timer - t0, "0.0 s")
End Sub

CountIFs in Excel with VBA is not counting decimal values

I have a problem with the function CountIF when used with decimals.
Below the code I have:
Sub Compair()
Dim I As Double
Row = 3
For I = 139.5 To 141.5 Step 0.25
Cells(Row, 3) = I
Cells(Row, 4) = Application.WorksheetFunction.CountIf(Range("A:A"), "<" & Cells(Row, 3))
Row = Row + 1
Next I
End Sub
And below the output:
It seems that the code functions good when it is compairing integer value and not with decimals.
PS: I do not want to loop on column A cells, as there could be more than 100k values and it will be so slowly
You can pull everything into variant arrays, which will be quicker on larger datasets than COUNTIFS:
Sub Compair()
With ActiveSheet
Dim rngArr As Variant
rngArr = Intersect(.UsedRange, .Range("A2", .Cells(.Rows.Count, 1))).Value2
Dim outArr() As Variant
ReDim outArr(1 To Int((141.5 - 139.5) / 0.25) + 1, 1 To 2)
Dim I As Double
Row = 1
For I = 139.5 To 141.5 Step 0.25
outArr(Row, 1) = I
outArr(Row, 2) = 0
Dim j As Long
For j = 1 To UBound(rngArr, 1)
If rngArr(j, 1) < I Then outArr(Row, 2) = outArr(Row, 2) + 1
Next j
Row = Row + 1
Next I
.Range("C3").Resize(UBound(outArr, 1), 2).Value = outArr
End With
End Sub
Your code is correct
just confirm your region, if you need the comas as decimal separatos configure your Windows format, and your excel format
Windows Format
https://www.windowscentral.com/how-change-date-and-time-formats-windows-10
Excel Format
https://edu.gcfglobal.org/en/excel2013/formatting-cells/1/
Sub Compair()
Range("A16").Select
Dim I As Double
Row = 16
For I = 139.5 To 141.5 Step 0.25
Cells(Row, 3) = I
Cells(Row, 4) = Application.WorksheetFunction.CountIf(Range("A:A"), "<" & Cells(Row, 3))
Row = Row + 1
Next I
End Sub

Is there a way to insert a certain amount of rows below a row that contains certain criteria?

I have a spreadsheet that contains data starting in row 2 column 1 and has 42 columns. I am trying to write a VBA code that will search all rows of my data starting with row 2 and if the value in column 32 is greater than 575, I need the code to insert enough rows below that row so that whatever the value was (whether it be 600 or 2,000) can be split into increments of 575. So for example, if row 5 column 32's value is 800, i want the code to add a row below row 5, and i want it to autofill the new row with the value of 575 in column 32 and replace the value in the original row with whatever it was minus 575. Also, in the first column of my data I have dates. For each new row that is created, I want it to be a week earlier than the date in the original row. Here is an example of what my data looks like:
Column1 ...Column 32.......Column 42
8/15/2019 // 3873
Here is what i want it to look like after I run the code.
Column1 ...Column 32......Column 42
8/15/2019 // 423
8/8/2019 // 575
8/1/2019 // 575
7/25/2019 // 575
7/18/2019 // 575
7/11/2019 // 575
7/4/2019 // 575
The slash marks are just there to show the separation in columns. And I want the data from all the other columns to stay the same as the row above. Is there a good way to do this?
This is the code I've come up with so far. However, the problem with it is I can't seem to figure out how to program it so that it know how many rows to add based on how large the quantity is. As of now, it just adds a row below any row that the value of column 32 is greater than 575. Also, it just adds blank rows. I don't have anything in my code that says what values to put in the newly created rows
Sub BlankLine()
Dim Col As Variant
Dim BlankRows As Long
Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim LargeOrder As Integer
Col = "AF"
StartRow = 1
BlankRows = 1
LargeOrder = 575
LastRow = Cells(Rows.Count, Col).End(xlUp).Row
Application.ScreenUpdating = False
With ActiveSheet
For R = LastRow To StartRow + 1 Step -1
If .Cells(R, Col).Value > LargeOrder Then
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
End If
Next R
End With
Application.ScreenUpdating = True
End Sub
As I mentioned before, I need the code to add however many rows needed to accommodate the original quantity to be broken into increments of 575, and also subtract a week with every row created. Thank you in advance for your help.
There are numerous way to achieve the objective. One is instead of reverse loop, you go down inserting balance amount and again on next row recalculated and so on till blank is encounters. May try the code tested with makeshift data
Option Explicit
Sub addLine()
Dim Col As Variant
'Dim BlankRows As Long
'Dim LastRow As Long
Dim R As Long
Dim StartRow As Long
Dim LargeOrder As Integer
Dim Ws As Worksheet
Dim ActNum As Double, Balance As Double
Set Ws = ThisWorkbook.ActiveSheet
Col = "AF"
StartRow = 2
'BlankRows = 1
LargeOrder = 575
R = StartRow
With Ws
ActNum = .Cells(R, Col).Value
Do While ActNum <> 0
If ActNum > LargeOrder Then
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Range(.Cells(R, 1), .Cells(R, 42)).Copy Destination:=.Cells(R + 1, 1)
.Cells(R + 1, 1).Value = .Cells(R + 1, 1).Value - 7
'simpler calculation
Balance = IIf(ActNum Mod LargeOrder > 0, Int(ActNum / LargeOrder) * LargeOrder, ActNum - LargeOrder)
'Balance = IIf(ActNum Mod LargeOrder > 0, Int(ActNum / LargeOrder) * LargeOrder, Int(ActNum / LargeOrder) * LargeOrder - LargeOrder)
.Cells(R + 1, Col).Value = Balance
.Cells(R, Col).Value = ActNum - Balance
End If
R = R + 1
ActNum = .Cells(R, Col).Value
Loop
End With
End Sub
Edit: may try the modified code below for the variance in requirement
Option Explicit
Sub addLine2()
Dim Col As Variant
Dim LastRow As Long
Dim R As Long, i As Long
Dim StartRow As Long
Dim RowtoAdd As Long
Dim Ws As Worksheet
Dim ActNum As Double, Balance As Double
Set Ws = ThisWorkbook.ActiveSheet
Col = "AS"
StartRow = 2
LastRow = Ws.Cells(Rows.Count, Col).End(xlUp).Row
R = StartRow
With Ws
Do
RowtoAdd = .Cells(R, Col).Value
LastRow = LastRow + RowtoAdd
For i = 1 To RowtoAdd
.Cells(R + 1, Col).EntireRow.Insert Shift:=xlDown
.Cells(R, 1).EntireRow.Copy Destination:=.Cells(R + 1, 1)
.Cells(R + 1, 1).Value = .Cells(R + 1, 1).Value - 7
.Cells(R + 1, 32).Value = ""
R = R + 1
Next i
R = R + 1
Loop Until R > LastRow
End With
End Sub

I am trying to sum the columns of specific headers in a particular row but I am getting total sum of all columns of that row irrespective of header

'I am trying to sum the columns of specific headers in a particular row but I am getting total sum of all columns of that row irrespective of header. Can someone please tell me my mistake?Please see the attached image for sample input output.
Dim DSum As Integer
Dim PSum As Integer
With wsn
NIMsLastRow = Worksheets("NIMSCarrierCount").Cells(Rows.Count, 1).End(xlUp).Row
NIMsLastCol = Worksheets("NIMSCarrierCount").Cells(1, Columns.Count).End(xlToLeft).Column
For j = 2 To NIMsLastRow
DSum = 0
PSum = 0
For k = 2 To NIMsLastCol
If .Cells(1, k).Value = "LTE 1900Deployed" Or "LTE 2500Deployed" Or "LTE 800Deployed" Or "UnassignedDeployed" Then
DSum = DSum + CInt(.Cells(j, k).Value)
End If
If .Cells(1, k).Value = "LTE 1900Planning" Or "LTE 2500Planning" Or "LTE 800Deployed" Or "UnassignedPlanning" Then
PSum = PSum + CInt(.Cells(j, k).Value)
End If
Next k
.Cells(j, NIMsLastCol + 1).Value = DSum
.Cells(j, NIMsLastCol + 2).Value = PSum
Next j
End With
I would consider a re-write to use Select Case which will also solve the error around your test conditions. Remember to use Option Explicit at the top of your module to check your variable declarations. And is it possible that you might need Double for your DSum and PSum? Note I have exchanged Integers for Longs to avoid potential overflow (happens with large numbers when trying to store something too big for the declared datatype)
Option Explicit 'Always use Option Explicit
Sub test()
Dim wsn As Worksheet
Set wsn = ThisWorkbook.Worksheets("NIMSCarrierCount") 'assumption this is correct sheet assigment
Dim DSum As Long 'use Long to avoid potential overflow
Dim PSum As Long
Dim NIMsLastRow As Long 'declare all variables
Dim NIMsLastCol As Long
Dim j As Long
Dim k As Long
With wsn
NIMsLastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
NIMsLastCol = .Cells(1, .Columns.Count).End(xlToLeft).Column
For j = 2 To NIMsLastRow
DSum = 0
PSum = 0
Dim testValue As String
For k = 2 To NIMsLastCol
testValue = .Cells(1, k)
Select Case testValue
Case "LTE 1900Deployed", "LTE 2500Deployed", "UnassignedDeployed"
DSum = DSum + CLng(.Cells(j, k))
Case "LTE 1900Planning", "LTE 2500Planning", "UnassignedPlanning"
PSum = PSum + CLng(.Cells(j, k))
Case "LTE 800Deployed"
DSum = DSum + CLng(.Cells(j, k))
PSum = PSum + CLng(.Cells(j, k))
End Select
Next k
.Cells(j, NIMsLastCol + 1).Value = DSum
.Cells(j, NIMsLastCol + 2).Value = PSum
Next j
End With
End Sub
Your if statement has been written incorrectly.
The Or "LTE 2500Deployed" is being evaluated to True for every query.
You need to specify fully for each parameter as such
.Cells(1, k).Value = "LTE 1900Deployed" Or .Cells(1, k).Value = "LTE 2500Deployed" Or...

Resources