Advance to Next Item from Within a For-Next Loop - vba

I have a For-Next loop. Within the loop I test several different criteria and if any test fails, then I am ready at that point to skip the remainder of the code in the loop and advance to the "next" item. The way I currently handle this is with a GoTo statement that takes me to the line right before "Next". I'd prefer not to use a GoTo statement, is there another way to advance to the "next" item from within a For-Next loop? TIA!
For x = 1 to 10
Test 1
If Test 1 fails then
GoTo Line1
End if
Test 2
If Test 2 fails then
GoTo Line1
End if
.
.
.
If all tests pass then
add item to array
End if
Line1:
Next

Unfortunately there is no continue-like statement in a for loop in vba. (The related control structure Exit For does exist but that's of no help here).
And it's good that you have reservations on using a GoTo: they do make code hard to follow.
Your best bet is to put the code in the loop in a separate function and use Exit Function within that function at appropriate points. You can even then relay error codes back to the caller so helping the code to scale.

Here's a workaround for the lack of a continue keyword:
For x = 1 to 10
Do
Test 1
If Test 1 fails then
Exit Do
End if
Test 2
If Test 2 fails then
Exit Do
End if
.
.
.
If all tests pass then
add item to array
End if
Loop While False
Next

You can use if else ladder :
For x = 1 to 10
if test 1
else if test 2
else if test 3
.
.
.
.
else
add item to array
end if
Next
Besides GoTo there is not any direct way to jump in between your code,
Hope this might help.

If you don't have too many tests, you could you use the Not condition and build a nested If statement. This should have almost the same effect as what you're asking for, since any failed test would end that If statement and move the code to the next X in your loop without executing the following tests.
Here's an example of what I mean -- a two test loop that builds an array containing the numbers 5 through 10:
Sub TestConditionalPass()
Dim intX As Integer
Dim intArray() As Integer
ReDim intArray(1)
For intX = 1 To 10
If Not intX -2 < 1 Then
If Not intX * 2 < 10 Then
ReDim Preserve intArray(UBound(intArray) + 1)
intArray(UBound(intArray)) = intX
End If
End If
Next intX
End Sub

Since you are preforming an action only if all tests succeed, then do the if-statement with ands. VBA doesn't short circuit the tests, (i.e. it will evaluate each test case, even if the first one returns false.) I would suggest encapsulating each test in a function that returns a Boolean to keep your code tidy.
If Test1() and _
Test2() and _
testn() THEN
add item to array
End if
Another way is to make use of a Boolean variable in your code, like this
Dim Success as Boolean
Success=True
Test 1
If Test 1 fails then
Success=false
End if
Test 2
If Test 2 fails then
Success=false
End if
.
.
.
If Success then
add item to array
End if
If the tests are expensive, you can add an inner if statement checking to see if we need to evaluate the next test like this
If Success Then
If Test n fails then
Success=false
End If
End if

Related

InvalidArgument=Value of ' ' is not valid for 'index' (' ' inside number)

This is my first question. I can't solve this error for 2 weeks.
In order to solve the problem signed up.
This is my vb code.
Try
For i As Integer = 0 To ListBox1.Items.Count - 1 Step 1
For j As Integer = 0 To ListBox2.Items.Count - 1 Step 1
If ListBox1.Items(i).ToString().Equals(ListBox2.Items(j).ToString()) = True Then
ListBox1.Items.RemoveAt(i)
End If
Next
Next
Catch ex As Exception
MsgBox("LOAD ERROR: " + ex.Message, vbCritical, "ERROR")
End Try
error :
InvalidArgument=Value of '20' is not valid for 'index'(' ' is varient.)
Project has no problems except for this error
Try this:
Dim items = ListBox1.Items.Where(Function(item) ListBox2.Items.Contains(item)).ToList()
For Each item in items
ListBox1.Remove(item)
Next
When I run your code, I receive a different exception, argument out of range...and that is caused by deleting items from an indexed collection while you're iterating through it. For example, let's say listbox1 has 10 items in it. If you find item number 1 in listbox2 and delete it, now you only have 9 items left in listbox1. The problem is, when you entered your loop, you told it to loop 10 items, and it will still try to do that. At some point, if any items are deleted, this loop will throw an exception...so you will need to change that sooner or later. To mitigate this, step through the collection that you'll be deleting items from backward like this:
For i As Integer = ListBox1.Items.Count - 1 to 0 Step -1
When I run the code with the change shown above, it works as intended and removes the duplicate items from listbox1. Unfortunately, I was unable to reproduce your invalid argument exception. It's odd to see that because usually that exception likes to pop up when using listviews, not listboxes. Perhaps you can edit your post and add a screenshot of the data in your listboxes so it's easier for other people to troubleshoot.
As you remove items from ListBox1 the total item count will decrease (obviously), however the For loop does not respect that. A For loop will only have the right side of To set once, which is done prior to the first iteration.
What you're currently doing is actually equal to this:
Dim a As Integer = ListBox1.Items.Count - 1
For i As Integer = 0 To a Step 1
Dim b As Integer = ListBox2.Items.Count - 1
For j As Integer = 0 To b Step 1
...
Next
Next
The fix for this is simple; create a variable that holds how many items you have removed, then, in an If-statement, check if i is more or equal to the current item count subtracted with how many item's you've removed. If so, exit the loop.
Dim ItemsRemoved As Integer = 0
For i As Integer = 0 To ListBox1.Items.Count - 1 Step 1
If i >= ListBox1.Items.Count - ItemsRemoved Then Exit For
For j As Integer = 0 To ListBox2.Items.Count - 1 Step 1
If ListBox1.Items(i).ToString().Equals(ListBox2.Items(j).ToString()) = True Then
ListBox1.Items.RemoveAt(i)
End If
Next
Next
For future reference you should also always remove/comment out the Try/Catch-statement so you can see where the error occurs and get more detail about it.
The point of my answer is that when you iterating any collection, you should NOT try to modify this collection. In for-loops you run into such trouble. But you can iterate using while-loop with no issues
Try
Dim index As Integer = 0
While index < ListBox1.Items.Count '!! this code based on fact that ListBox1 item Count changes
For j As Integer = 0 To ListBox2.Items.Count - 1 ' <- this is ok because ListBox2 doesn't chage
If string.Equals(ListBox1.Items(index).ToString(), ListBox2.Items(j).ToString()) Then
ListBox1.Items.RemoveAt(index)
Continue While ' no index increase here because if you remove item N, next item become item N
End If
Next
index += 1
End While
Catch ex As Exception
MsgBox("LOAD ERROR: " + ex.Message, vbCritical, "ERROR")
End Try
This is good example of how things actually work. And it shows few techniques
I just selected Build-->Clean solution and it cleaned out the bad elements. This occurred as a result of adding and deleting menu items, without deleting the subroutines of the deleted menu items. As soon as I cleaned the solution, and then ran the project, the error was gone.

Exiting a VBA loop with multiple variables

As a newbie in VBA (Excel), I am trying to make a tool, which determines what the diagnostic yield of a certain test must be; in order to be cost-effective as a pre-screening to another diagnostic test.
What I want it to do is calculate for a certain yield of test A, at what yield for test B the costs per diagnosis are the same for both tests. The code I wrote has to loop for a certain range for the diagnostic yield and exit this loop when the costs per diagnosis for test A drop under the costs per diagnosis for test B.
However, the code keeps looping for this range, but does not stop when my condition on costs is met. I tried a lot, including do while and do until statements, but it just won't work. I really hope someone could help me out! Many thanks in advance! Kirsten
Sub TGP_WES_OR_WES()
Dim Yield_A As Double
Dim Yield_B As Double
Dim Yield_A_max As Double
Dim Cost_diagnosis_A As Double
Dim Cost_diagnosis_B As Double
Yield_B = Range("C6")
Yield_A_Max = Yield_B - 0.1
Cost_diagnosis_B = Range("E15")
Cost_diagnosis_A = Range("E11")
Do While Yield_A < Yield_A_max
For Yield_A = 1 To Yield_A_max Step 0.1
Range("C5").Value = Yield_A
If Cost_diagnosis_A < Cost_diagnosis_B Then
Exit For
End If
Next Yield_TGP
Loop
Range("D1").Value = Yield_TGP
End Sub
You have a double loop (both of which appear to be doing the same thing):
Do While Yield_A < Yield_A_max
For Yield_A = 1 To Yield_A_max Step 0.1
...
Next Yield_TGP
Loop
Remove the outer do loop for starters.
The 2nd issue I see is that your loop exit conditions do not appear to depend on the loop iteration. That is to say Cost_diagnosis_A and Cost_diagnosis_B are not updated or changed by the loop. This generally indicates a design error as nearly all loop termination conditions will be dependent upon a value the loop is calculating or updating (or overall loop progress). Intuitively, your loop termination condition should incorporate Yield_A either directly or indirectly (from a downstream calculation). Perhaps you want to be updating the values of Cost_diagnosis_A and/or Cost_diagnosis_B inside your loop body, based on Yield_A?

Are if statements considered as an Iteration

Hello I'm just wondering whether an 'if' statement can be considered as an Iteration. Because Iteration is used until a certain criteria is meet to allow the code to continue.
An iteration is one cycle (one time through) a loop. If blocks are not loops.
For blocks or While blocks are loops, and executing one cycle of the contents of one of those blocks — what's inside the For block or the While block — is one iteration of the loop. If the entirety of a For block or While block is a conditional If block, then the If block could be one iteration... but not because it's an If block, but because it's what's inside the loop.
No, an iteration is a repetition of some code. Once doesn't count as a repetition, else all code would be considered an iteration.
The If statement by itself cannot be considered iteration. You can run a code block containing an if statement as many times as you wish, but this doesn't make the if statement an iterator by itself. It's what calls that code block which could stand for iteration.
If statement has some similarities with those other conditional statements :
Select Case
Try Catch
Explicit iterators are the followings :
For/Next - For Each/Next
Do/Loop
While/End While
However, you have some logic that could turn the usage of an if statement as the trigger to resume or stop conditions of a loop, without the usage of direct loop statements like For or While. But that doesn't turn that if statement an iterator by itself, because the iteration is the combination of the if statement and a specific logic allowed by the programming language.
Recursion :
Private Function ResumeIncrement(ByRef Number As Int32) As Boolean
Number = Number + 1
If Number < 10 Then
Return ResumeIncrement(Number)
Else
Return False
End If
End Function
GoTo statement :
Private Sub TestGoTo()
Dim Number As Int32 = 0
IncrementMore:
Number = Number + 1
If Number < 10 Then
GoTo IncrementMore
End If
End Sub
And as stated by Joel Coehoorn above, the If block can contain anything, including iterator blocks triggered by For, While or Do. If you remove the contained block, that doesn't change anything in what the if part is supposed to do : a conditional check !
You can also do the inverse, and use an If block to control the way an iteration behaves. Like :
If SomeCondition Then
Exit For
' Or Exit While, etc. ie, using the 'Exit' statement
End If
or
i = 0
Do
i = i + 1
' ^^ capture i in Debug or Console
' Control the value of i...
If i Mod 2 = 0 Then
i = i - 2
Else
i = i * 3
End If
Loop While i < 30
' i = 1, 4, 3, 10, 9, 28, 27, [Exit Do with i = 81]
In the two examples above, the If statement is either there to break the iteration, or try again, that said, to "control the iteration", but in no means, to stand for iterator by itself.
Here is also another answer to clear out one uncertainty. You asked
Are if statements considered as an Iteration ?
(You made "if statements" plural) Then you said
Hello I'm just wondering whether an 'if' statement can be considered
as an Iteration. Because Iteration is used until a certain
criteria is meet to allow the code to continue.
(then you made it singular.. ???) That "...Iteration is used until..." should self answer your question alone, assuming you're iterating code blocks (which is the way it is understood here)
But what if you're talking about iterating some possible values until one meets your requirements..?
If MyValue = "" Then
' ...
ElseIf MyValue.StartsWith("A") Then
' ...
ElseIf MyValue.ToUpper = "TEST" Then
' ...
Else
' ...
End If
is similar to :
Select Case True
Case MyValue = "":
' ...
Case MyValue.StartsWith("A")
' blah blah blah
End Select
or
If MyValue = "" Then GoTo EmptyString
If MyValue.StartsWith("A") Then GoTo StartsWithA
If MyValue.ToUpper() = "TEST" Then GoTo IsTEST Else GoTo DoNothing
EmptyString:
' ...
GoTo DoNothing
StartsWithA:
' ...
GoTo DoNothing
IsTEST:
' ...
GoTo DoNothing
DoNothing:
' Resume...
Etc.
That's not an iteration ! That's a list of evaluation that are processed in order. When a condition evaluates to True, the related code is executed. Unlike iteration, this is just a matter of selecting the right path and resume, not walking the very same path several times (loops)
Yes, you're iterating through a collection of possible values (condition states) but NO, you're not iterating the If statement. You're only checking the result of each single If test once.

Condetional break inside the For loop and continue execution

How can i break the loop and continue execution from the next line comes after the loop, if condition inside the loop is/are true. i tried labels and GoTo but it will executed all time will not depend on the condition inside the loop.
i have the following code :
Dim i As Integer
For i = 1 To 50
If i > 35 Then
' break the loop
End If
Next
I had tried with GoTo it works properly, some times it execute by default doese not depend on the condition given inside the loop
If i > 35 Then
GoTo lbl
End If
lbl: ' code comes here
Thank you......
If i > 35 Then
Exit For
End If
However your code with Goto would work too - provided the lbl: is outside the loop.
You can use the Exit command for this kind of break ; Exit Sub is used to comes out from a particular Sub, Exit Function will help you to comes out from a function, here you can use Exit For hence your code will be like the following
Dim i As Integer
For i = 1 To 50
If i > 35 Then
Exit For
End If
Next

What's Wrong With my logic in this Vb program?

I'm new to visual basic and I just started taking classes on it at school. I was given an assignment to write an app that tells if an input in a textbox is a Prime Number Or Not.
I have written this code snippet in Visual Studio:
Public Class PrimeNumberApp
Public Sub CheckButton_Click(sender As Object, e As EventArgs) Handles CheckButton.Click
Dim x, y As Integer
x = Val(PrimeTextBox.Text)
For y = 2 To (x - 1)
Select Case x
Case Is = (33), (77), (99)
MsgBox("Its not a prime number, try a different number!")
Exit Sub
End Select
If x Mod y = 0 Then
MsgBox("Its not a prime number, try a different number!")
Exit Sub
Else
MsgBox("Its a prime number, you're golden!")
Exit Sub
End If
Next
Select Case x
Case Is <= 0
MsgBox("I'm only accepting values above 0. :p")
Exit Sub
End Select
End Sub
I have this code snippet displaying a Message Box telling whether the input by the user is a prime number or not with the help of the Mod operator in visual basic.
If you go through the code carefully, you'll notice I had to separately create more or less escape statements for the number 33, 77 and 99.
I had to do this cause every time I typed either of those three numbers, I'd get the result that the number is a prime number which is incorrect since they're all divisible by numbers apart from themselves. Without getting things mixed up, the program displays other prime and non-prime numbers with the right Message Box, just those three.
I had spent hours trying to figure out what I had done wrong before I added the lines below to put myself out of vb misery, lol.
Select Case x
Case Is = (33), (77), (99)
MsgBox("Its not a prime number, try a different number!")
Exit Sub
End Select
But doing this isn't healthy if I really want to be awesome at coding. So here's my question, how do I get this program fixed to display the right message box when any integer is typed in the text box. Thanks.
Your code doesn't seem to work at all. It seems to always say that every number is a prime number. You loop through every possible divisor and in that loop you test to see if the input is evenly divisible by the current divisor. That part makes sense. The problem, though is that you always immediately exit the loop after the first iteration regardless of the result of the test. Pay close attention to this code from inside your loop:
If x Mod y = 0 Then
MsgBox("Its not a prime number, try a different number!")
Exit Sub
Else
MsgBox("Its a prime number, you're golden!")
Exit Sub
End If
If we remove all of the spurious details, you can tell that the logic is flawed:
If MyTest = True Then
Exit Sub
Else
Exit Sub
End If
As you can see, no matter what the result of the test is, it's always going to exit the loop. Therefore, the loop will never progress beyond the first iteration. In order to make it continue testing every possible divisor, you need to remove the Exit Sub statements and just keep track of the result in a variable. For instance, something like this:
Dim isPrime As Boolean = True
For y = 2 To x - 1
If XIsEvenlyDivisibleByY Then
isPrime = False
Exit For
End If
Next
If isPrime Then
' ...
Else
' ...
End If
Notice that once the first evenly divisible divisor is found, I have it Exit For. There's no point in searching any further since one is enough to make it not prime.
For what it's worth, you should use Integer.TryParse rather than Val and you should use MessageBox.Show rather than MsgBox. Val and MsgBox are old VB6 style functions. It's preferable to use the newer .NET-only versions.
here is an example of how to find prime numbers, for example up to 100, using 2 loops to check every single number in a given range. In this case I started by 2 because as you know 2 is the smallest of the prime numbers.
Dim i As Integer = 2, j As Integer = 2
For i = 2 To 100
For j = 2 To (i / j)
If i Mod j = 0 Then
Exit For
End If
Next
If (j > (i / j)) Then
Console.WriteLine("{0} is prime", i)
End If
Next