Excel Macro Goes into Endless Loop - excel

I've been working on this macro and function for a while. The function works fine when I just run it by itself, but when I try to put it into a For loop it repeats forever and never increments. I'll start with the main macro.
Sub Run()
Dim shrt As Worksheet: Set shrt = Worksheets("Shortages")
Dim sTbl As ListObject: Set sTbl = shrt.ListObjects("Shortages_T")
Dim xRng As Range
Dim x As Integer
shrt.Range("H3") = "Can Build"
'x = 9
For x = 9 To 14 Step 1
Set xRng = sTbl.ListColumns(x).DataBodyRange
shrt.Cells(3, x) = BldQty(xRng, x)
Next x
End Sub
If I comment out the loop and manually increment x, it works fine. Once I put it in the loop, it just endlessly repeats and never increments. I've checked with a Print.Debug. The function below is what I'm trying to call with it. I checked the debug and it just keeps running the same range and column over and over again.
Function BldQty(xRng As Range, scol As Integer) As Long
Dim shrt As Worksheet: Set shrt = Worksheets("Shortages")
Dim sTbl As ListObject: Set sTbl = shrt.ListObjects("Shortages_T")
Dim use As Worksheet: Set use = Worksheets("Useage")
Dim uTbl As ListObject: Set uTbl = use.ListObjects("UseTable")
Dim Cell As Range
Dim mdl As String
Dim cRng As Range
Dim qp As Double
Dim div As Double
Dim low As Double
Dim col As Integer
BldQty = xRng(1)
scol = scol - 1
For Each Cell In xRng
If Application.WorksheetFunction.Min(xRng) <= 0 Then
BldQty = 0
Exit For
Else
mdl = Cell.Offset(0, -scol).Value
'VLOOKUP equivalent
qp = uTbl.ListColumns("Component").DataBodyRange.Find(mdl).Offset(0, 1)
div = Cell / qp
If div < BldQty Then
BldQty = div
End If
End If
Next Cell
End Function
Am I calling it wrong in the loop?

ByRef instead of ByVal?
I'm not sure about what your code shall do, but
shrt.Cells(3, x) = BldQty(xRng, x)
goes to
Function BldQty(xRng As Range, scol As Integer) As Long
where scol ist passed by reference
And in this function you change scol so it returns its new value to x
Try
Function BldQty(xRng As Range,ByVal scol As Integer) As Long
ByRef is the default in VBA

Related

Excel - Test if two consecutive digits in the range's address are the same

I want to offset a range if the numerical part of a range's address can be divided by 11.
(A11, A22, A33, and so forth).
Take a range in a given sheet, for example Range("A2").
Could I do ...
Dim isRng as Range
Dim rngAddress as String
Dim tstAddress as Integer, nsnAddress as Integer
isRng = Range("A2")
isRng.Select
rngAddress = isRng.Address
Currently, rngAddress = $A$2 (I think). So then, could I ...
tstAddress = Right(rngAddress, 2)
nsnAddress = Right(tstAddress, 1)
If tstAddress / nsnAddress = 11 Then
'whatever code
Set isRng = ActiveCell.Offset(4,0).Select
Else
End If
I want it to skip down 4 rows after hitting any range like A11 or A22.
Would this work? Is there a better way of doing this? I really appreciate the help.
This should do the trick...
Sub sully_was_here()
Dim r As Range
Set r = [a22]
With r
.Select
If .Row Mod 11 = 0 Then
'whatever code here
.Offset(4).Select
End If
End With
End Sub
Divisible: Using Mod
If cCell.Row Mod 11 = 0 Then
Option Explicit
Sub Divisible()
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range: Set rg = ws.Range("A2:A33")
Dim cCell As Range
Dim r As Long
For Each cCell In rg.Cells
r = r + 1
If cCell.Row Mod 11 = 0 Then
Debug.Print r, cCell.Offset(4).Address, "***"
Else
Debug.Print r, cCell.Address
End If
Next cCell
End Sub

Using the LinEst function and return values in a column of variable length

I am trying to use the LinEst function to take values from a range of rows of data and input them into a new sheet under some headings. I only want to do this for a particular number of rows (up to row number defined as "c". My VBA skills are very basic.
Sub Button7_Click()
Sheets.Add.Name = "Down Sweep Power Law"
Dim xrng As Range, yrng As Range
Dim i As Long
Dim Rng As Range
Dim l As Long
Dim k As Long
Dim i2 As Long
Dim c As Long
Dim j As Long
Dim drop As Range
Dim drop2 As Range
Dim DownSweep As Chart, UpSweep As Chart, cht As Chart
Dim ws As Worksheet, smallest
Dim dsws As Worksheet
Set ws = Worksheets("Template") '<< use variables for worksheets!
Set dsws = Worksheets("Down Sweep Power Law")
Set Rng = ws.Range(ws.Range("B11"), ws.Range("B11").End(xlDown))
smallest = WorksheetFunction.Small(Rng, 1)
l = Rng.Find(what:=smallest, LookIn:=xlValues, lookat:=xlWhole).Row
k = Rng.Rows.Count
c = l - 10
Set xrng = ws.Range("C11:CP11")
Set yrng = ws.Range("C201:CP201")
Set drop = dsws.Range("A2")
Set x2rng = xrng.Offset(1, 0)
Set y2rng = yrng.Offset(1, 0)
Set drop2 = drop.Offset(1, 0)
dsws.Range("A1").Value = "(n-1) Value"
dsws.Range("B1").Value = "log(k) Value"
dsws.Range("C1").Value = "(n-1) Value"
dsws.Range("D1").Value = "n Value"
dsws.Range("E1").Value = "R Value"
If i < c Then
Set drop = Application.LinEst(Log10(yrng), Log10(xrng), True, False)
i = i + 1
End If
ITERATE:
If i < c Then
Set drop2 = Application.LinEst(Log10(y2rng), Log10(x2rng), True, False)
x2rng = x2rng.Offset(1, 0)
y2rng = y2rng.Offset(1, 0)
drop2 = drop2.Offset(1, 0)
i = i + 1
GoTo ITERATE
End If
End Sub
the code runs but when I go on the created sheet, there is a #NAME error (2029) and no values are present.
Is there a way to fix this?
Any help would be appreciated.
I think you have omitted a step from your plan. LinEst returns an array and you want to assign the values in that array to the range Drop. You can't assign the array directly to the range. Please try this code.
Option Explicit
Sub Button7_Click()
Dim xrng As Range, yrng As Range
Dim Drop As Range
Dim Arr As Variant ' LinEst result array
Dim Rng As Range
Dim R As Long
Dim l As Long
Dim k As Long
Dim i2 As Long
Dim c As Long
Dim j As Long
Dim DownSweep As Chart, UpSweep As Chart, cht As Chart
Dim ws As Worksheet, Smallest As Variant
Dim dsws As Worksheet
Set ws = Worksheets("Template") '<< use variables for worksheets!
Sheets.Add.Name = "Down Sweep Power Law"
Set dsws = Worksheets("Down Sweep Power Law")
Set Rng = ws.Range(ws.Range("B11"), ws.Range("B11").End(xlDown))
Smallest = WorksheetFunction.Small(Rng, 1)
l = Rng.Find(what:=Smallest, LookIn:=xlValues, LookAt:=xlWhole).Row
k = Rng.Rows.Count
c = l - 10
Set xrng = ws.Range("C11:CP11")
Set yrng = ws.Range("C201:CP201")
Set Drop = dsws.Range("C2:CP2").Offset(0, -2)
dsws.Range("A1").Value = "(n-1) Value"
dsws.Range("B1").Value = "log(k) Value"
dsws.Range("C1").Value = "(n-1) Value"
dsws.Range("D1").Value = "n Value"
dsws.Range("E1").Value = "R Value"
Do While R < c
Arr = Application.LinEst(Log10(yrng), Log10(xrng), True, False)
Drop.Value = Arr ' or perhaps: = Application.Transpose(Arr)
Set xrng = xrng.Offset(1, 0)
Set yrng = yrng.Offset(1, 0)
Set Drop = Drop.Offset(1, 0)
R = R + 1
Loop
End Sub
I don't know what kind of array LinEst will return. You may have to transpose the result.
I also tried to improve your management of ranges. However, the code is entirely untried, for lack of data. There may be logical errors in my code as well as typos but the syntax should be sound. It may not take you all the way over the finish line but I hope it will help you in your quest.

How fix object required error in vba in with statement?

I want to create a button that, when the user presses it, will do this:
before press the button user mark name in pivot table on sheet Eff
after press button the same name will be filter in another pivot table on sheet Pivot_All
I tried this, but in .PivotItems(a(g)).Visible = True it gives me object required error
Public Function GetLength(a As Variant) As Integer
If IsEmpty(a) Then
GetLength = 0
Else
GetLength = UBound(a) - LBound(a) + 1
End If
GL = GetLength
End Function
Public Function a(ByVal rng As Range) As Variant
Dim f As Long, r As Range
ReDim arr(1 To rng.Count)
f = 1
For Each r In rng.Cells
arr(f) = r.Value
f = f + 1
Next r
a = arr
End Function
Sub Macro6()
Dim rngCopy As Range
Dim rngPaste As Range
Dim rng As Range
Set rng = Selection
Sheets("Pivot_All").Activate
ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl").ClearAllFilters
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl")
For i = 1 To .PivotItems.Count - 1
.PivotItems(.PivotItems(i).Name).Visible = False
Next i
End With
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Empl")
For g = 0 To GL
.PivotItems(a(g)).Visible = True
Next g
End With
End Sub
a is waiting for a range a(ByVal rng As Range) but you submit a number g as parameter. You must submit a Range object instead.
Also GL is not defined in your procedure, I assume you wanted to use GetLength instead.
So it should be something like:
Dim ResultOfA As Variant
ResultOfA = a(Range("your range here"))
For g = 0 To GetLength(ResultOfA)
.PivotItems(ResultOfA(g)).Visible = True
Next g
I recommend always to activate Option Explicit: In the VBA editor go to Tools › Options › Require Variable Declaration.
Note that you can read a continous range into an array in one line:
Dim ArrValues() As Variant
ArrValues = Range("A1:A10").Value
Debug.Print ArrValues(1, 5) '= A5
If you use this you might not need your function a anymore.
You would end up with something like:
Option Explicit
Public Sub Macro6()
Dim rngCopy As Range
Dim rngPaste As Range
Dim rng As Range
Set rng = Selection
Dim ws As Worksheet
Set ws = Worksheets("Pivot_All")
With ws.PivotTables("PivotTable1").PivotFields("Empl")
.ClearAllFilters
Dim i As Long
For i = 1 To .PivotItems.Count - 1
.PivotItems(.PivotItems(i).Name).Visible = False
Next i
Dim ResultOfA As Variant
ResultOfA = a(rng)
If Not IsEmpty(ResultOfA) Then
Dim g As Long
For g = LBound(ResultOfA) To UBound(ResultOfA)
.PivotItems(ResultOfA(g)).Visible = True
Next g
End If
End With
End Sub
Public Function a(ByVal rng As Range) As Variant
Dim f As Long, r As Range
ReDim arr(1 To rng.Count)
f = 1
For Each r In rng.Cells
arr(f) = r.Value
f = f + 1
Next r
a = arr
End Function

Convert column letter to number

I get raw data from querys into Excel, and when preforming VLOOKUP's sometimes I have to count or calculate by hand what column I am going to refer to.
I want a calculator were I type in userform textbox ex: "M", and the other textbox will show the correct column number for "M" (13).
My userform looks like this:
I have come up with something like the code below, I dim every letter as an integer and when that is typed in to the textbox it will add each others values.
I don't know how to code the CommandButton1_click "Räkna".
Private Sub CommandButton1_Click()
'how do i transform letters into numbers here?
End Sub
Sub raknare()
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim d As Integer
Dim e As Integer
Dim f As Integer
Dim g As Integer
Dim h As Integer
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim l As Integer
Dim m As Integer
Dim n As Integer
Dim o As Integer
Dim p As Integer
Dim q As Integer
Dim r As Integer
Dim s As Integer
Dim t As Integer
Dim u As Integer
Dim v As Integer
Dim w As Integer
Dim x As Integer
Dim y As Integer
Dim z As Integer
Set a = 1
Set b = 2
Set c = 3
Set d = 4
Set e = 5
Set f = 6
Set g = 7
Set h = 8
Set i = 9
Set j = 10
Set k = 11
Set l = 12
Set m = 13
Set n = 14
Set o = 15
Set p = 16
Set q = 17
Set r = 18
Set s = 19
Set t = 20
Set u = 21
Set v = 22
Set w = 23
Set x = 24
Set y = 25
Set z = 26
End Sub
To get info from a dialogue to a variable, you'd do something like
Dim v As Variant
v = Application.InputBox(Prompt:="Letter: ", Title:="Letter", Type:=2)
If CBool(v) Then ' The inputbox returns "false" if cancel is pressed
...
EndIf
If you want to use a userform instead, you'd do something like
Dim s As String
s = UserForm1.TextBox1.Text
To get the column number from its name, you can either do something like what's described in this answer.
Or do what I've done in my office, and do the arithmetic yourself:
I solved my problem! But not the way I initially intended. I took some old code i had from a previous project and made it work on this issue to.
I made a sheet called "DATA" and inserted column A with the alphabet A to CW, and next to that i have the corresponding number for each letter 1-100.
Then i made a search function that looks like this:
Sub rakna()
Dim rSearch As Range
Dim rFound As Range
With Sheets("DATA")
Set rSearch = .Range("A1", .Range("A" & Rows.Count).End(xlUp))
Set rFound = rSearch.Find(What:=TextBox1.Text, LookIn:=xlValues)
If rFound Is Nothing Then
TextBox2.Value = ""
Else
TextBox2.Value = rFound.Offset(0, 1).Value
End If
End With
End Sub
Now I dont need to calculate the columns no more I can just type the one I need in my textbox!
You could try:
Option Explicit
Sub test()
Dim Letter As String
Dim LetterNumber As Long
Letter = "F"
LetterNumber = Range(Letter & 1).Column
End Sub

VBA dynamic row lookup while looping

I'm very new to VBA and should probably spend some time on debugging and learning the formalities of how code should be written.
I am using a loop that uses the Hlookup function to populate a table from on one sheet from data on a master sheet. (This is in the Sub SetMatrix). Within the Sub that performs this task I use some other UDF's, one which copies and pastes the variables (names from a 3rd sheet which may change) I want to lookup from the master sheet.
In any case it runs perfectly fine when the I use a hardcoded number for the row in the lookup function. However, once I try to use a variable (jpmRow instead of a number like 50) for the row it will work the first time only. Then when I run it again I get RunTime error 91 - object variable or withblock variable not set. The debugger take me back to the DynamicRange UDF, Set StartCell line, which confuses me because that is not where I am setting the row variable. Meanwhile if I use a constant for the row it lets me rerun the sub with success every time.
Here is the code:
Option Explicit
Dim wsTemplate As Worksheet
Dim ws As Worksheet
Dim TxtCell As Range
Dim PortfolioCell As String
Dim StartCell As Range
Dim EndCell As Range
Dim RangeParameter As Range
Dim jpmRow As Integer
Dim myColumn As Integer
Dim myRow As Integer
Function DynamicRange(TxtToFind As String) As Range
Dim k As Integer
k = iCount
Set ws = Sheets("Template")
Set StartCell = ws.Cells.Find(TxtToFind).Offset(2, 0)
myColumn = StartCell.Column
myRow = StartCell.Row
Set EndCell = ws.Cells(myRow + k - 1, myColumn)
Set DynamicRange = ws.Range(StartCell.Address, EndCell.Address)
'Set DynamicRange = RangeParameter
End Function
Function iCount() As Integer
Set ws = Sheets("Template")
Set StartCell = ws.Cells.Find("Ticker").Offset(2, 0)
Set EndCell = ws.Cells.Find("Total").Offset(-1, 0)
iCount = ws.Range(StartCell.Address, EndCell.Address).Rows.Count
End Function
Sub SetMatrix()
Dim StartTable As Range
Dim iRows As Range
Dim iColumns As Range
Dim myArray(50, 50) As Integer
Dim wsJPM As Worksheet
Dim i As Integer
Dim j As Integer
Set StartTable = Sheets("Correlation Matrix").Range("A3")
Set iRows = Range(StartTable.Offset(1, 0).Address, StartTable.Offset(iCount, 0).Address)
Set iColumns = Range(StartTable.Offset(0, 1).Address, StartTable.Offset(0, iCount).Address)
Set wsJPM = Sheets("JPM")
Sheets("Correlation Matrix").Cells.ClearContents
Sheets("Correlation Matrix").Cells.ClearFormats
DynamicRange("Asset Class").Copy iRows
DynamicRange("Asset Class").Copy
iColumns.PasteSpecial Transpose:=True
For i = 1 To iCount
For j = 1 To iCount
jpmRow = wsJPM.Cells.Find(StartTable.Offset(i, 0), SearchOrder:=xlColumns, LookAt:=xlWhole).Row
StartTable.Offset(i, j).Value = Application.WorksheetFunction.HLookup(StartTable.Offset(0, j), Sheets("JPM").Range("B1:BZ100"), jpmRow, False)
Next j
Next i
End Sub

Resources