VB: List(Of List(Of String)) keeps changing the content of the outer list when I change the inner one? - vb.net

I'm writing a program in VB, and I need to make a list of lists (I've already figured out how to do that one). The problem is, the outer list is going to need a different number of elements depending on other variables elsewhere in the program.
I've looped this code:
Dim rep As Long = 1023
Dim items As List(Of String)
items.Add("First Entry")
items.Add("Second Entry")
items.Add("Third Entry")
items.Add("Fourth Entry")
'(sake of argument, these are the variables
'that will be changing vastly earlier
'in the program, I put them in this way to simplify
'this part of my code and still have it work)
Dim myList As New List(Of List(Of String))
Dim tempList As New List(Of String)
For index = 1 To Len(rep.ToString)
tempList.Add(items(CInt(Mid(rep.ToString, index, 1))))
Next
myList.Add(tempList)
tempList.Clear()
My issue is with that last part; every time I add the tempList to myList, it's fine, but when I clear tempList, it also clears the version of tempList in myList.
myList will have a count of 1, but the list inside it has a count of 0 as soon as I clear tempList. And I have to clear tempList because I'm looping this section of code over and over, a variable number of times.
Is there a way around this? Am I being a horrible noob?

You're using the same tempList each time, instead of making a new one.
You likely need to do:
myList.Add(tempList)
tempList = new List(Of String) ' Create a new List(Of T), don't reuse...

Related

Value of type List(Of Long) cannot be converted to List(Of ListItem)

I am filling data from removeActiveService (list of listItem) into a new list listRemove. However listRemove is turning into a list of Long.
Dim listRemove = removeActiveService.Select(Function(item) item.Text.Replace(serviceRemove, "") And item.Text.Split("-"c)(0).Trim And item.Value.Split("-"c)(1).Trim).ToList()
If I change it to Dim listRemove As List(Of ListItem), it results in the error
Value of type List(Of Long) cannot be converted to List(Of ListItem)
I need to perform replace and split on the text and value (see my code). What is the correct syntax here so that it can be a List(Of ListItem)?
EDIT
Starting with checkboxlist items in removeActiveService
Copy that into a brand new list of items called listRemove.
I need to perform these on the TEXT of the list items in listRemove
item.Text.Replace(serviceRemove, "")
item.Text.Split("-"c)(0).Trim
And I need to perform this on the VALUE of the list items in listRemove
item.Value.Split("-"c)(1).Trim
If you expect to output a List(Of ListItem) then you need to create ListItem objects somewhere, which you're not doing now. You're performing operations on the input data but you're not doing anything useful with the results. And doesn't do anything that could be considered useful there. If you expect to output a list of ListItem objects containing the results of those operations then you actually have to create ListItem objects containing the result of those operations. As far as I can tell, this would be the way to go:
Dim listRemove = removeActiveService.Select(Function(item) New ListItem With
{
.Text = item.Text.Replace(serviceRemove, "").Split("-"c)(0).Trim(),
.Value = item.Value.Split("-"c)(1).Trim()
}).
ToList()

Is there a method to add to a list (of list(of String)) byVal?

I'd like to repeatedly add lists of strings to a bigger list collection, but it seems that the list.add function is adding the items byref. How can I change the below example code to pass the elements byval so that they're not cleared when I re-use variables:
Dim inner As New List(Of String)
Dim outer As New List(Of List(Of String))
Dim eleCount As Integer = 0
lbltop:
inner.Add("a")
inner.Add("b")
inner.Add("c")
outer.Add(inner)
Debug.Write(outer(0)(0).ToString())
inner.Clear()
Debug.Write(outer(0)(0).ToString())
eleCount += 1
If eleCount < 2 Then
GoTo lbltop
End If
this writes a then there's an out of range exception for the next debug.write statement.
I'd like it to write aa then loop to add another inner element.
List<T> is a reference type. Only way to not effect it when calling Clear() is to add a clone of it to outer instead. You can do so by using .ToList()
outer.Add(inner.ToList())
But I actually think the most clear way is to instead assign inner a new List instead of calling Clear, than you dont have to do above.
inner = new List(Of String)

How to build up this list and randomize it?

I am making a quiz for my computer science class and the basic concept is that you have 15 keywords and 15 definitions. All need to be randomly displayed and the correct answer has to appear. The user has to match the correct definition to the keyword twice and then that keyword and definition are not displayed again. When all have been answered twice the quiz is over.
I have stored both my keywords and my definitions in the same file so they don't get out of sync. The text file looks like so:
Keyword1 = Definition1
Keyword2 = Definition2
Keyword3 = Definition3
etc (Total of 15)
My main form looks like this:
Public Class quiz
Private Sub quiz_load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles myBase.Load
Dim MyList As List(Of KeyValuePair(Of String, String)) = New List(Of String, String))
For Each line As String In System.IO.File.ReadAllLines("my-file-path")
Dim Pair() As String = line.split("=")
mylist.add(New KeyValuePair(Of String, String)(Pair(0), Pair(1)))
Next
I am displaying the random keyword in a label and the definitions in radiobuttons. Two need to be random definitions and one has to be the correct definition to the keyword shown, which also needs to be displayed randomly.
What I am asking is:
How do I finish this list off as it is overwriting the other 15 lines only using the last one?
How can I randomize the list of keywords and definitions for when they are displayed?
How can I remove the items when each keyword has been matched to its definition twice? E.G: Keyword 1 and definition 1 have been answered correctly twice so remove from list so it won't be displayed again.
This should give you an idea:
Const NUMBER_OF_ANSWERS As Integer = 3
Dim kv As New Dictionary(Of String, String)
kv.Add("Keyword1", "Definition1")
kv.Add("Keyword2", "Definition2")
kv.Add("Keyword3", "Definition3")
Dim r As New Random
Dim kvRandom As List(Of KeyValuePair(Of String, String)) =
kv.OrderBy(Function() r.Next).ToList
'questions will appear in random order
For Each line As KeyValuePair(Of String, String) In kvRandom
Dim keyword As String = line.Key
Dim correctDefinition As String = line.Value
Dim keywords As New List(Of String)
keywords.Add(keyword)
keywords.AddRange(kv.Keys.Except({keyword}).
OrderBy(Function() r.Next).Take(NUMBER_OF_ANSWERS - 1))
Dim definitionsRandom As List(Of String) =
keywords.Select(Function(x) kv(x)).OrderBy(Function() r.Next).ToList
'TODO: need to write some code here
'display keyword and three possible definitions to the user
'(out of which one is correct)
'answers will also appear in random order
'Check answer against value stored in "correctDefinition"
Next
The code is pretty much self-explanatory, if you have any questions, please let me know in comments.
EDIT: Here is how you can populate your dictionary from a file.
'assuming file structure is like this:
'keyword1,definition1
'keyword2,definition2
'keyword3,definition3
'...
For Each line As String In IO.File.ReadAllLines("keywords_and_definitions.txt")
Dim parts() As String = line.Split(",")
kv.Add(parts(0), parts(1))
Next
After you figure out your loading issues, you can follow this basic algorithm to do the rest of your tasks. I don't want to give you exact code for a class assignment. Figuring that kind of thing out is half the fun of learning programming.
Loop through each of the questions, keeping an index to the current
question.
Declare a new list of answers and place the correct answer
from the current question in to the list.
Pick 3 more answers
randomly that are NOT the correct answer, and answers than have been
picked for this question and add them to the answer list.
Randomize the answer list.
Display the questions and answers
Check the chosen answer against the answer for the current question.
Repeat for every question.

Why does VB.NET behave like this with lists?

This is a question concerning a list of lists.
Dim smallList As New List(Of Integer)
Dim largeList As New List(Of List(Of Integer))
smallList.Add(3)
largeList.Add(smallList)
smallList.Clear()
smallList.Add(4)
largeList.Add(smallList)
In this code, I would expect largeList to add the list (3) to itself, and then to add the list (4) to itself. But instead of storing the data inside smallList, it seems to store a reference smallList instead, so ends up containing ((4), (4)), which is not what I want.
Why does it do this, and how can I get around it? Thanks.
When you have a list of reference types, you actually have a list of references. Adding something to the list doesn't mean that the data is copied, it's just the reference that is added to the list.
To add separate objects to the list, you have to create a new object for each item, and as lists are reference types themselves, that goes for lists too.
Dim smallList As List(Of Integer) ' just a reference at this time
Dim largeList As New List(Of List(Of Integer))
smallList = New List(Of Integer)() ' The first list
smallList.Add(3)
largeList.Add(smallList)
smallList = New List(Of Integer)() ' Here's another list
smallList.Add(4)
largeList.Add(smallList)

Generic Lists copying references rather than creating a copiedList

I was developing a small function when trying to run an enumerator across a list and then carry out some action. (Below is an idea of what I was trying to do.
When trying to remove I got a "Collection cannot be modified" which after I had actually woken up I realised that tempList must have just been assigned myLists reference rather than a copy of myLists. After that I tried to find a way to say
tempList = myList.copy
However nothing seems to exist?? I ended up writing a small for loop that then just added each item from myLsit into tempList but I would have thought there would have been another mechanism (like clone??)
So my question(s):
is my assumption about tempList receiving a reference to myList correct
How should a list be copied to another list?
private myList as List (Of something)
sub new()
myList.add(new Something)
end sub
sub myCalledFunction()
dim tempList as new List (Of Something)
tempList = myList
Using i as IEnumerator = myList.getEnumarator
while i.moveNext
'if some critria is met then
tempList.remove(i.current)
end
end using
end sub
By writing tempList = myList you don't make a copy oh the collection, you only make tempList reference myList. Try this instead : dim tempList as new List (Of Something)(myList)
I think if you called myCalledFunction(byVal aListCopy as Something) you can let the framework do the work.
If your list consists of value types you can just create a new list with the old list passed in the constructor. If you are going to be doing a deep copy of a reference object your best bet is to have your reference type implement ICloneable (example). You can then loop through and clone each object or you could add an extension method (like this c# example).
Try this - use LINQ to create a new list from the original, for example:
Sub Main()
Dim nums As New List(Of Integer)
nums.Add(1)
nums.Add(2)
nums.Add(3)
nums.Add(4)
Dim k = (From i In nums _
Select i).ToList()
For Each number As Integer In nums
k.Remove(number)
Next
End Sub
k will then be a new list of numbers which are not linked to the source.