excel vba : why is this function looping infinitely? - excel

I've written the following function to search a worksheet for the last available blank row.
Function findlastLog_Row() As Integer
Dim i As Integer
i = 1 'start at row 1
Do Until Sheets("Log").Cells(i, 1) = ""
i = i + 1
Loop
findlastLog_Row = i
End Function
Any ideas why its looping over and over. It seems to start all over on the second to last line findlastLog_Row = i. This last line is to return the value of i. Am I oversimplifying this?

Is this what you are trying?
Sub Sample()
Debug.Print findlastLog_Row
End Sub
Function findlastLog_Row() As Long
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Log")
With ws
findlastLog_Row = .Range("A" & .Rows.Count).End(xlUp).Row
End With
End Function

Try changing it to Sheets("Log").Cells(i, 1).Value (hence the .Value at the end). .Cells() will return a Range object. I'm not entirely sure what the default property of a range is, but it might just not be the .Value property.

Related

Fill cells with values from another sheet using For Loop VBA

I have a set of information in the same column (H27:O27) in one sheet ("P1-FR1") and would like to paste individual values to another sheet (AQ6:AX6) ("Übersicht GESAMT")
I'm trying to use a For loop but the values just copy one after the other (in the same cell) instead of copying one in each cell. This is my code:
Sub CopyValues()
Dim i As Long
Dim j As Long
Dim Wert As Long
For i = 8 To 14
Wert = Sheets("P1-FR1").Cells(27, i)
For j = 43 To 50
Sheets("Übersicht GESAMT").Cells(6, j) = Wert
Next j
Next i
End Sub
You don't need a double For loop in this case at all. A simple .Value copy will work. The code below shows two examples with different ways to accomplish what you want. (TIP: it always helps me to be VERY clear on how I name the variables, it helps to keep track of where all the data is coming and going)
Option Explicit
Sub CopyTheValues()
Dim datenQuelle As Range
Dim datenZiel As Range
Set datenQuelle = ThisWorkbook.Sheets("P1-FR1").Range("H27:O27")
Set datenZiel = ThisWorkbook.Sheets("Übersicht GESAMT").Range("AQ6:AX6")
'--- method 1 - works because the ranges are the same size and shape
datenZiel.Value = datenQuelle.Value
'--- method 2 - for loops
' index starts at 1 because the Range is defined above
' (and we don't care what rows/columns are used)
Dim j As Long
For j = 1 To datenQuelle.Columns.Count
datenZiel.Cells(1, j).Value = datenQuelle.Cells(1, j).Value
Next i
End Sub
Copying By Assignment
Option Explicit
Sub CopyValuesNoLoop()
ThisWorkbook.Worksheets("Übersicht GESAMT").Range("AQ6:AX6").Value _
= ThisWorkbook.Worksheets("P1-FR1").Range("H27:O27").Value
End Sub
Sub CopyValuesQuickFix()
Dim j As Long: j = 43
Dim i As Long
For i = 8 To 14
ThisWorkbook.Worksheets("Übersicht GESAMT").Cells(6, j).Value _
= ThisWorkbook.Worksheets("P1-FR1").Cells(27, i).Value
j = j + 1
Next i
End Sub
The nesting of the for loops is causing your issue. It is causing each cell from the first sheet to be copied to all cells on the second sheet.
You only need one loop to perform the copy. Something like this should work.
Sub CopyValues()
Dim i As Long
For i = 8 To 15
Sheets("Übersicht GESAMT").Cells(6,i+35) = Sheets("P1-FR1").Cells(27,i)
Next i
End Sub

VBA: How to improve code with for and if loops

Looking for the best way to write the following code. I am currently struggling to make my code as simple and neat as possible. The code effectively takes a range and returns back the range which is non-empty.
Option Explicit
Sub ReturnNonEmptyRange()
Dim testBool As Boolean
Dim i As Long
testBool = True
For i = 2 To 10000:
If Range("G" & i) = "" Then
i = i - 1
testBool = False
End If
If testBool = False Then
Exit For
End If
Next i
MsgBox ("The range is G2:K" & i)
End Sub
Below is some sample code you can try.
The function LastUsedRow is not used, but I'm providing since it can be useful. This will return the last used row in your worksheet.
Using "Range" like you did above will assume you want to use active sheet. I always like to specify a workbook and a sheet so there is no ambiguity.
Sub Test()
' Start at row 1 and and stop when first blank cell found
Dim wks As Worksheet: Set wks = ThisWorkbook.Worksheets("Sheet1")
Dim row As Long
' Option 1: using column numbers
row = 1
Dim col As Long: col = 7 ' G
Do Until wks.Cells(row + 1, col).Value = ""
row = row + 1
Loop
MsgBox ("Last used row (in column): " & row) ' assumes something in row 1
' Option 2: using column letters
row = 1
Dim colLetter As String: colLetter = "G"
Do Until wks.Range(colLetter & row + 1).Value = ""
row = row + 1
Loop
MsgBox ("Last used row (in column): " & row) ' assumes something in row 1
End Sub
Public Function LastUsedRow(wks As Worksheet) As Long
Dim rng As Range: Set rng = wks.UsedRange ' Excel will recalc used range
LastUsedRow = rng.row + rng.Rows.Count - 1
End Function
I think your method only works if your none-empty range is consecutive. Suppose G2:G10 is non-empty, G11 is empty and G12:G20 is non-empty. Your code would come to i=11 and return G2:K10 as the non-empty range.
A more reliable, and quicker way to find the last non-empty cell (before row 1000) would be this:
range("G1000").End(xlUp).row
This will give you the first non-empty row in column G above row 1000. If row 1000 is non-empty however, it would search upwards for the last non-empty row. so you might want to change it to:
Range("G" & Rows.Count).End(xlUp).Row
This will find the last non-empty row, starting from the bottom of the worksheet.
How about combining the loop's exit conditions all into the loop control header.
I also would explicitly access the range()'s value to be more clear in the code and check the string length to be zero.
Option Explicit
Sub ReturnNonEmptyRange()
Dim testBool As Boolean
Dim i As Long
testBool = True
i = 2
While (i < 10000) And (Len(Range("G" & i).Value) <> 0)
i = i + 1
Wend
MsgBox ("The range is G2:K" & i)
End Sub
In the case this was an Array, one could not use Range("G" & Rows.Count).End(xlUp).Row. I believe #Siddharth provided a good solution. The downside being it will stop at a non- empty row.
Sub ReturnNonEmptyRange()
Dim i As Long
For i = 2 To 10000:
If Len(Trim(Range("G" & i).Value)) = 0 Then Exit For
Next i
MsgBox ("The range is G2:K" & i - 1)
End Sub

For Loop statement VBA Excel 2016

Proper syntax Match and If not isblank
I need some assistance with creating a loop statement that will determine the range start and end where a particular criteria is met.
I found these statements on the web and need help to modify them to loop thru two different worksheets to update a value on 1 of the worksheets.
This one has an issue returning True or False value for the Range when I want to pass the actual named range for look up where this field = Y, then returns the value from another column. I original tried using Match and If is not blank function. But that is very limiting.
See the previous post to see what I am trying to accomplish - I know I will need to expand the code samples and probably will need help with this modification.
Sub Test3()
Dim x As Integer
Dim nName As String
Sheets("BalanceSheet").Select
nName = Range("qryDifference[[Validate Adjustment]]").Select
Debug.PrintnName
' Set numrows = number of rows of data.
NumRows = Range(nName, Range(nName).End(xlDown)).Rows.Count
' Select cell a1.
' Establish "For" loop to loop "numrows" number of times.
For x = 1 To NumRows
' Insert your code here.
MsgBox"Value found in cell " & ActiveCell.Address
' Selects cell down 1 row from active cell.
ActiveCell.Offset(1, 0).Select
Next
End Sub
This is what I have so far - this is giving me and issue with
ActiveCell.Offset(4, 0).Select
nAgentNo = Range("qryDifference[[agtno]]").Value
nValidate = Range("ryDifference[[Difference]]").Value
Debug.Print nAgentNo
Debug.Print nValidate
Type mismatch error on the above.
Sub Revised_AgentAmount()
Dim myRange As Range
Dim i As Long, j As Long
Dim nAgentNo As String
Dim nValidate As Long
Sheets("BalanceSheet").Select
Set myRange = Range("qryDifference[[Validate Adjustment]]")
For i = 1 To myRange.Rows.Count
For j = 1 To myRange.Columns.Count
If myRange(i, j).Value = "Y" Then
ActiveCell.Offset(4, 0).Select
nAgentNo = Range("qryDifference[[agtno]]").Value
nValidate = Range("ryDifference[[Difference]]").Value
Debug.Print nAgentNo
Debug.Print nValidate
End If
Next j
Next i
End Sub
In your first statement you declare nName as a String then try to select it. You would need to declare it as a Range if you are going to use it as a Range object.
I found solution elsewhere with a if statement instead of the for loop.
=IF([#agtno]=B24,[#[agt_amt]],SUMPRODUCT((Balance!$B$2:$B$7=[#agtno])*(Balance!$F$2:$F$7="Y")*Balance!$E$2:$E$7)+[#[agt_amt]])

Excel VBA :: Find function in loop

I'm trying to loop through several worksheets that contain some source data that has to be copied to one main sheet, called "PriorityList" here.
First of all, the sub is not working and I think the error is somewhere in the "find"-method. Second, the sub takes quite long to run, and I think this is maybe because the "find"-method searches through the whole sheet instead of only the relevant range?
Thank you very much for your answers!
Patrick
Sub PriorityCheck()
'Sub module to actualise the PriorityList
Dim CurrWS As Long, StartWS As Long, EndWS As Long, ScheduleWS As Long
StartWS = Sheets("H_HS").Index
EndWS = Sheets("E_2").Index
Dim SourceCell As Range, Destcell As Range
For CurrWS = StartWS To EndWS
For Each SourceCell In Worksheets(CurrWS).Range("G4:G73")
On Error Resume Next
'Use of the find method
Set Destcell = Worksheets(CurrWS).Cells.Find(What:=SourceCell.Value, After:=Worksheets("PriorityList").Range("A1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
'Copying relevant data from source sheet to main sheet
If Destcell <> Nothing Then
Destcell.Offset(0, 2).Value = SourceCell.Offset(0, 5).Value + Destcell.Offset(0, 2).Value
If SourceCell.Offset(0, 3).Value = "x" Then Destcell.Offset(0, 3).Value = "x"
End If
End If
On Error GoTo 0
Next SourceCell
Next CurrWS
End Sub
here short sample how to use 'Find' method to find the first occurrence of the source.Value in the priorityList.
Source cell is one of the cells from the range "G4:G73" and priorityList is used range on "PriorityList" sheet. Hope this helps.
Public Sub PriorityCheck()
Dim source As Range
Dim priorityList As Range
Dim result As Range
Set priorityList = Worksheets("PriorityList").UsedRange
Dim i As Long
For i = Worksheets("H_HS").Index To Worksheets("E_2").Index
For Each source In Worksheets(i).Range("G4:G73")
Set result = priorityList.Find(What:=source.Value)
If (Not result Is Nothing) Then
' do stuff with result here ...
Debug.Print result.Worksheet.Name & ", " & result.Address
End If
Next source
Next i
End Sub
Here is an approach using arrays. You save each range into an array, then iterate through array to satisfy your if-else condition. BTW IF you want to find the exact line with code error, then you must comment On Error Resume Next line.. :) Further, you can simply store the values into a new array, dump everything else into the main sheet later after iterating through all the sheets instead of going back and forth to sheets, code, sheets..code..
Dim sourceArray as Variant, priorityArray as Variant
'-- specify the correct priority List range here
'-- if multi-column then use following method
priorityArray = Worksheets(CurrWS).Range("A1:B10").Value
'-- if single column use this method
' priorityArray = WorkSheetFunction.Transpose(Worksheets(CurrWS).Range("A1:A10").Value)
For CurrWS = StartWS To EndWS
On Error Resume Next
sourceArray = Worksheets(CurrWS).Range("G4:J73").Value
For i = Lbound(sourceArray,1) to UBound(sourceArray,1)
For j = Lbound(priorityArray,1) to UBound(priorityArray,1)
If Not IsEmpty(vArr(i,1)) Then '-- use first column
'-- do your validations here..
'-- offset(0,3) refers to J column from G column, that means
'---- sourceArray(i,3)...
'-- you can either choose to update priority List sheet here or
'---- you may copy data into a new array which is same size as priorityArray
'------ as you deem..
End If
Next j
Next i
Next CurrWS
PS: Not front of a MS Excel installed machine to try this out. So treat above as a code un-tested. For the same reason I couldn't run your find method. But it seems odd. Don't forget when using match or find it's important to do proper error handling. Try checking out [find based solutions provided here.
VBA in find function runtime error 91
Excel 2007 VBA find function. Trying to find data between two sheets and put it in a third sheet
I have edited the initial code to include the main logic using two array. Since you need to refer to values in J column of source sheets, you will need to adjust source array into a two-dimensional array. So you can do the validations using first column and then retrieve data as you desire.
For everyone maybe interested, this is the code version that I finally used (pretty similar to the version suggested by Daniel Dusek):
Sub PriorityCheck()
Dim Source As Range
Dim PriorityList As Range
Dim Dest As Range
Set PriorityList = Worksheets("PriorityList").UsedRange
Dim i As Long
For i = Worksheets("H_HS").Index To Worksheets("S_14").Index
For Each Source In Worksheets(i).Range("G4:G73")
If Source <> "" Then
Set Dest = PriorityList.Find(What:=Source.Value)
If Not Dest Is Nothing Then
If Dest <> "" Then
Dest.Offset(0, 2).ClearContents
Dest.Offset(0, 2).Value = Source.Offset(0, 5).Value + Dest.Offset(0, 2).Value
End If
If Source.Offset(0, 3).Value = "x" Then Dest.Offset(0, 3).Value = "x"
Debug.Print Dest.Worksheet.Name & ", " & Dest.Address
End If
End If
Next Source
Next i
MsgBox "Update Priority List completed!"
End Sub

check if a range is empty in vba module

I wanted to check if an excel range in empty in a section of code in user module.
I used the below code
Worksheets(yearsheet).Range("N" & rownum & ":DI").Select
If Application.WorksheetFunction.CountA(Selection) = 0 Then
Exit Sub
End If
I'm getting runtime error 1004. Can anyone tell whats my mistake?
Thanks in advance.
PS: rownum is integer variable and yearsheet is string variable. both these variables were updated properly in code prior to the above section of the code
"N" & rownum & ":DI" doesn't evaluate to a real address because it's missing the row number for the second half of the address. Also, you should avoid using Select statement whenever possible.
Assuming the whole range is in one row, this would work:
Sub test()
Dim yearsheet As String
Dim rownum As Integer
yearsheet = "Sheet2"
rownum = 2
If Application.WorksheetFunction.CountA(Worksheets(yearsheet) _
.Range("N" & rownum & ":DI" & rownum)) = 0 Then
Exit Sub
End If
End Sub
The best way to test if a selection is (not) empty in VBA:
' Tests if a selection of cells exists.
' #return true or false
Function isCellSelection() As Boolean
Dim r As range
isCellSelection = False
Set r = Selection.Cells
If IsEmpty(r) Then
isCellSelection = True
End If
End Function ' isCellSelection

Resources