VB.net For Each Loop with If Statement Error - vb.net

When this code is ran, Visual Studio gives the error:
InvalidOperationException was unhandled
List that this enumerator is bound to has been modified. An enumerator can only be used if the list does not change.
Dim counter As Integer
For Each x In lstWinners.Items
If x = lstWinners.SelectedItem Then
counter += 1
End If
Next
Here's a screenshot:
http://i.cubeupload.com/lIoWDg.png
EDIT:
This can be fixed by adding this line at the beginning:
Dim anything as string = lstWinners.Text
But why does this error happen, and why does this fix it?

When you are going over the list with for each it kind of "locks" the array. A good way around this is to just copy the array for the iteration.
Just use Array.Copy(source, target, target.Length) where your source would be lstWinners.Items and target is an array you declare. Then do your for each loop on the array. Something like:
Dim counter As Integer
Dim tempcopy(lstWinners.Items.Count) As String
Array.Copy(lstWinners.Items, tempcopy, tempcopy.Length)
For Each x In tempcopy
If x = lstWinners.SelectedItem Then
counter += 1
End If
Next

Perhaps it is seeing the = operator as the assignment operator instead.
Try this code instead:
Dim counter As Integer
For Each x In lstWinners.Items
If x Is lstWinners.SelectedItem Then
counter += 1
End If
Next
Is explicitly compares equality on objects, so it removes any potential ambiguity.

Assuming that your items are strings and you want to count the items with the same text then you could use
Dim counter As Integer
Dim x = lstWinners.SelectedItem.ToString()
counter = lstWinners.Items.Cast(Of String).Count(Function(z) z = x)
However, your code should not give that error unless there is something else that is running and modify your list (a separate thread?)

Related

Why does this ArrayList work with no declaration in Visual Basic?

I'm working off some example code in Word 2010, but it breaks after 10 added variables. I'm trying to understand ArrayLists in VB now.
If (ActiveDocument.Name = "template.docm") Then
With ActiveDocument
On Error Resume Next
.Variables.Add Name:="1", Value:="1"
.Variables.Add Name:="2", Value:="2"
I think the code starts adding objects to an ArrayList, but everything I've read on VBA Arraylists require declaration like:
Dim Variables As Object
Set Variables = CreateObject("System.Collections.ArrayList")
If I follow the pattern and create more objects using .Variables.add then the index breaks after 10. Right now I'm just trying to understand the list.
From Vincent G. - "Variables is a collection property of the object Document, not an ArrayList, and does not seems to break after 10 variables were added, at least on my system."
This helped me figure out the problem was not declaring the array/collection.
My actual problem was iterating through a later loop. I previously had:
Dim z As String
int i = 1
For Each f In myarray
.Variables(i).Value = f
i = i + 1
Next f
and the parameter passed for indexing Variables is actually a string
.Variables(name)
so:
int i = 1
For Each f In myarray
z = CStr(i)
.Variables(z).Value = f
i = i + 1
Next f
fixed my problem, thanks to everyone who took time to help!

Declaring Array() in VBA-ACCESS does not work without upper limit

I am learning about declaring arrays. It works when I declare it by giving an upper limit using following code:
Dim arrayA(5) as String
I check it by assigning a random value:
arrayA(0) = 1
MsgBox arrayA(0)
MsgBox responds by giving a value of 1.
However, my actual intention is to create a dynamic array that I defined as below:
Dim arrayA() as String
I test it in the same way
arrayA(0) = 1
MsgBox arrayA(0)
But this time it does not work and MsgBox pops up empty. Would someone tell me if I need to load some libraries to work with dynamic array?
Arrays in VBA need to be initialized before they are used.
You can initialize an array with a Redim statement:
Dim arrayA() as String
Dim i As Integer
i = 0
Redim ArrayA (0 To i)
arrayA(0) = "1" 'String
MsgBox arrayA(0)
Alternatively, there are functions that return an initialized array. In that case, Redim is not needed as the initialization happens in the external function. You do need to make sure you match type with the array being returned, though, and the overhead is the same or more.
Dim arrayA() as Variant
arrayA = Array(1)
MsgBox arrayA(0)
You can declare an array without a limit, but must redim the array to the desired limit prior to using it:
Dim myArray() As Long
Redim myArray(0)
myArray(0) = 0 'etc...
So, you cannot use a "dynamic" array in VBA like this.
The best reference I've ever known for arrays (and much other VB/A related information), comes from the late Chip Pearson: http://www.cpearson.com/Excel/VBAArrays.htm

Find a variable with concatenation

I would like to find a variable with concatenation.
Exemple :
Dim oExcelRangeArray1(0, 0) As Object
Dim oExcelRangeArray2(0, 0) As Object
Dim oExcelRangeArray3(0, 0) As Object
For i As Integer = 1 To 3
oExcelRangeArray & i = xl.Range("A1:Z400").Value
Next
but oExcelRangeArray & i doesn't work.
Thank you
For the extent of my knowledge, there is no way to achieve what you are trying to do directly, because oExcelRangeArray & i will not be evaluated as a separate step before the variable assignment happens.
In my mind you have two choices:
Assign each variable individually,
oExcelRangeArray1 = x1.Range("A1:Z400").Value
oExcelRangeArray2 = x1.Range("A1:Z400").Value
oExcelRangeArray3 = x1.Range("A1:Z400").Value
oExcelRangeArray4 = x1.Range("A1:Z400").Value
Or, add each array to a list, and iterate through it,
Dim oExcelRangeArrayList As New List(Of Object)
oExcelRangeArrayList.Add(oExcelRangeArray1)
oExcelRangeArrayList.Add(oExcelRangeArray2)
oExcelRangeArrayList.Add(oExcelRangeArray3)
oExcelRangeArrayList.Add(oExcelRangeArray4)
For i As Integer = 0 To 3
oExcelRangeArrayList(i) = x1.Range("A1:Z400").Value
Next
[Note: Writing this freehand without checking it, code may not be verbatim; hopefully you get the concept. Corrections welcome.]

build an array of integers inside a for next loop vb.net

i got this far ... my data string, "num_str" contains a set of ~10 numbers, each separated by a comma. the last part of the string is a blank entry, so i use '.Trim' to avoid an error
Dim i As Integer
Dim m_data() As String
m_data = num_str.Split(",")
For i = 0 To UBound(m_data)
If m_data(i).Trim.Length > 0 Then
MsgBox(Convert.ToInt32(m_data(i).Trim))
End If
Next i
as you can see from the Msgbox, each of the numbers successfully pass through the loop.
where i am stuck is how to place all of the 'Convert.ToInt32(m_data(i).Trim)' numbers, which are now presumably integers, into an array.
how do i build an array of integers inside the For / Next loop so i can find MAX and MIN and LAST
TIA
You just need to initialize the array with the zero-based indexer. You can deduce it's initial size from the size of the string():
Dim m_data = num_str.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
Dim intArray(m_data.Length) As Int32
For i = 0 To m_data.Length - 1
intArray(i) = Int32.Parse(m_data(i).Trim())
Next i
Note that i've also used the overload of String.Split which removes empty strings.
This way is more concise using the Select LINQ Operator.
Dim arrayOfInts = num_str.
Split({","c},
StringSplitOptions.RemoveEmptyEntries).
Select(Function(v) Int32.Parse(v.Trim()))
Dim minInt = arrayOfInts.Min()
Dim maxint = arrayOfInts.Max()

Error Reading string into array

I'm in the process of creating a tile based game. This game requires a method to load a text file and write the numbers between the delimiter "-", to a multidimensional array. However, an error message "object not set to an instance occurs.
'Load map
Public Sub LoadMap(ByVal URI As String)
Using reader As New System.IO.StreamReader(URI)
For x As Integer = 0 To 13
Dim line = reader.ReadLine()
Dim tokens() As String = line.Split("-")
'adds values to multidimensional array
For y As Integer = 0 To 16
Me.map(x, y) = Integer.Parse(tokens(y))
Next y
Next x
End Using
End Sub
Example map - numbers represent image id's
2-2-2-0-0-0-0-0-0-0-0-3-3-5-5-5-5
2-2-2-0-0-0-0-0-0-0-0-3-3-5-5-5-5
2-2-2-0-0-0-0-0-0-0-0-3-3-2-2-2-5
2-2-2-0-0-0-0-0-0-0-0-3-3-2-2-2-5
2-2-2-0-0-0-0-0-0-0-0-3-3-2-2-2-5
0-0-0-0-0-0-0-0-0-0-0-3-3-2-2-2-5
3-3-3-3-3-3-3-3-3-3-3-3-3-3-3-3-3
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
4-4-4-4-4-3-4-4-4-4-4-4-4-2-2-2-2
I can't seem to establish the problem. Thanks in advance...
Always use Option Strict On! (Your code shouldn’t even compile, you need to parse the input integers, or your map stores strings, which is equally as bad since you logically store numbers.)
Name your variables properly.
Omit vacuous comments. Only comment things that aren’t obvious from the code.
Don’t hard-code magic numbers (what’s 11? 8?)
Don’t declare variables before initialising. Declare on first use.
The outer loop in your code makes no sense.
Don’t close streams manually, always use a Using block to ensure the program still works in the face of exceptions.
Initialise map.
Which leaves us with:
Public Sub LoadMap(ByVal URI As String)
Const MapHeight As Integer = 12
Const MapWidth As Integer = 9
Me.map = New Integer(MapHeight, MapWidth) { }
Using reader As New System.IO.StreamReader(URI)
For x As Integer = 0 To MapHeight - 1
Dim line = reader.ReadLine()
Dim tokens() As String = line.Split("-")
For y As Integer = 0 To MapWidth - 1
Me.map(x, y) = Integer.Parse(tokens(y))
Next y
Next x
End Using
End Sub
Bonus: Check for errors: what if the map doesn’t have the predefined width/height? Why hard-code this at all?
osRead.ReadLine() returns Nothing if the end of the input stream is reached. You call Peek to see if you're at the end of the input, but then, you proceed to read 12 lines without checking if you're at the end of the input in-between. If you have less than 12 more lines, you'll get the error you mentionned on temp.Split("-"), because temp will have a value of Nothing, so you can't call methods on it.
Also, just noticed something... your Map is 11x8, but you're reading 12 lines, and going through 9 values, you probably want to do:
For x As Integer = 0 To 10
or
For x As Integer = 1 To 11
Same thing for your other loop.
If temp is null (Nothing) in VB.Net, then you can't call methods on it.
Do a check for Nothing before attempting to do anything with the value.
So Just to be sure you have updated your code to look something like this:
If temp IsNot Nothing
'tempLine stores the split read line
Dim tempLine() As String
'splits readline into - ERROR Not set to an instance
tempLine = temp.Split("-")
'adds values to multidimensional array
For y As Integer = 0 To 8
Me.map(x, y) = tempLine(y)
Next y
End If
And you are STILL getting the null reference exeption?
also make sure that your StreamReader is properly initialized... if the initialization fails (perhaps because of a bad URI) then attempting to call osRead.peek() will throw the "object not set to an instance" error.