Generic Lists copying references rather than creating a copiedList - vb.net

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.

Related

VB.net Apply function to items in a list

I have a list of string containing full file paths and I'd like to apply a function to each path in that list and get the result in the same or a new list.
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Function FileName(spth As String) As String
'Returns filename with extension from full path
Return System.IO.Path.GetFileName(spth)
End Function
The end result I'd like is for the list Remove to contain the following. I know I could use a loop to do this but I've been learning about lambda expressions lately and feel there should be a simple solution to this.
{"045-0201.iam", "212-D017.ipt", "211-W01.iam"}
Try this
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Remove = Remove.Select(Function(s)
Return IO.Path.GetFileName(s)
End Function).ToList
Calling Select and ToList on the existing List is most likely fine and what most people would do. It's worth being aware, though, that that will not modify the existing collection but rather return a new one. If you only have the one reference to that list then that's not a big deal but other references to the existing list will not see the change, e.g.
Dim fileNames As New List(Of String) From {"C:\Folder\File1.ext",
"C:\Folder\File2.ext",
"C:\Folder\File3.ext"}
Dim temp = fileNames
fileNames = fileNames.Select(Function(s) Path.GetFileName(s)).ToList()
For Each fileName In fileNames
Console.WriteLine(fileName)
Next
For Each fileName In temp
Console.WriteLine(fileName)
Next
If you run that then you'll see that the first loop displays just the files names but the second loop displays the full paths, because it still refers to the original list.
If that's a problem, there is another way to do this without an explicit loop:
Dim fileNames As New List(Of String) From {"C:\Folder\File1.ext",
"C:\Folder\File2.ext",
"C:\Folder\File3.ext"}
Dim temp = fileNames
Array.ForEach(Enumerable.Range(0, fileNames.Count).ToArray(),
Sub(i) fileNames(i) = Path.GetFileName(fileNames(i)))
For Each fileName In fileNames
Console.WriteLine(fileName)
Next
For Each fileName In temp
Console.WriteLine(fileName)
Next
If you run that then you'll see that both loops display just the file names because there's only one list.
That said, if the first code posed a problem because of multiple references to the list, I'd just use a loop.
I know you stated that you'd want something other than a loop, but there really is no needfor anything fancy here. By the way, writing Remove.Add sounds like a riddle.
Sub Main()
Dim Remove As New List(Of String)
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Assemblies\045-0201.iam")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\212-D017.ipt")
Remove.Add("C:\_Vault\Designs\Jobs\Customer\Job23\Parts\211-W01.iam")
Console.WriteLine("Before execution")
For Each s As String In Remove
Console.WriteLine(s)
Next
For i As Integer = 0 To Remove.Count - 1
Remove(i) = MyFunction(Remove(i))
Next
Console.WriteLine("After execution")
For Each s As String In Remove
Console.WriteLine(s)
Next
Console.ReadLine()
End Sub
Private Function MyFunction(path As String) As String
Return IO.Path.GetFileName(path)
End Function
This outputs:

Return values from Dictionary key

I'm a bit of a vb.net noob - apologies if this is a silly question.
I have a collection named Applications and these store objects of type application.
Dim Applications As New Dictionary(Of String, Application)()
Each application is created and added to this collection using
Public Sub New(dbid As String, name As String, status As String, mode As String)
csv_dbid = dbid
csv_app = name
csv_status = status
csv_mode = mode
End Sub
See the image i've included which shows in the debug/output my collection created with the objects and values associated.
I want to know a way I can access a Key and return all the corresponding values of csv_dbid, csv_app, csv_status and csv_mode. I've been googling for a bit and struggling.
Many thanks in advance.
Gary Waddell
In the case of the posted screenshot you would do:
Dim app = Applications("100")
app Will be a HealthCareApplication like any other so you can just use it as you would:
app.csv_dbid = "123"
You can also just refer to the dictionary item without a variable:
Applications("100").csv_dbid = "123"
To find out if the dictionary knows of a key, use the ContainsKey method. This can be particularly useful if you're iterating a colection and want to handle duplicates:
For Each thing in someList
if Applications.ContainsKey(thing.Key) Then
HandleDuplicate(thing)
Else
Applications(thing.Key) = New HealthCareApplication
End if
'It certainly exists in the collection now and this won't crash with KeyNotFound
Applications(thing.Key).csv_dbid = thing.DbId
Next thing
If you enumerate a dictionary you get a collection of KeyValuePair, the Key is (in this case) the "100" you used as the indexer. The Value is a HealthCareApplication type
For Each kvp in Applications
console.Write(kvp.Key)
Console.Write(kvp.Value.csv_dbid)
Next kvp

Retrive all Global variables declared (VB.NET)

I'm trying to list all global variables declared in my code in a "passive" way, i mean, not manually creating a List(of T) and then add one by one into the list.
I have this custom Object, lets say Car.vb, i declared a lot of them as global and them populated all the "Cars" one by one in the load event. But some where in my code i want to perform a loop in all the "Cars", ok I could manage a List(of T) but if tomorrow i add 4 more cars in the Global scope, i must to add manually this 4 cars into the List(of T) too, other wise this new 4 cars will not be part of the loop. So i started to think if there's some way to list all variable of a specific type declared in code.
Any hint? Thanks!
Finally got how to do this:
Dim Cars_List As New List(Of Car)
Dim assbly As Assembly = [Assembly].GetExecutingAssembly()
Dim types As Type() = assbly.GetTypes()
For Each t As Type In types
If t.Name = "Form1" Then
For Each p As FieldInfo In t.GetFields()
If p.FieldType.Name = "Car" Then
Console.WriteLine(p.Name & " - " & p.FieldType.Name)
Cars_List.Add(CallByName(Me, p.Name, vbGet))
End If
Next
End If
Next

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

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...

Clone a List(Of Class)

I've done some reading and cant seem to wrap my head around what the best approach would be to clone a List(of class) in my VB2010 project. I have three classes that are related like so
Public Class City
'here are many fields of type string and integer
Public Roads As New List(Of Road)
End Class
Public Class Road
'here are many fields of type string and integer
Public Hazards As New List(Of Hazard)
End Class
Public Class Hazard
Implements ICloneable
'here are many fields of type string and integer and double
Public Function Clone() As Object Implements System.ICloneable.Clone
Return Me.MemberwiseClone
End Function
End Class
So lets say I have a city I'm working on, there are cases where I want to create, as a base one road and its hazards, then add another road but using the prior roads hazards as a starting point and then tweak the fields.
Dim rd As New Road
'add road fields
dim hz1 as New Hazard
'add hazard fields
dim hz2 as New Hazard
'add hazard fields
'add the hazard objects to the road
rd.Hazards.Add(hz1)
rd.Hazards.Add(hz2)
'add the road to the city
myCity.Roads.Add(rd)
'here I want to start a new road based on the old road
Dim rdNew As New Road
'copy or clone the hazards from old road
rdNew.Hazards = rd.Hazards '<============
'over-write some of the hazard fields
rdNew.Hazards(0).Description = "temp"
So I know that copying a class will copy the pointer and not the contents. I used the ICloneable interface in the hazard class but cant say I'm using it right. The Hazards variable is a list of Hazard class. How would i go about cloning that class?
Implementing IClonable doesn't mean that it replaces the regular assignment, it will still just copy the reference. And you aren't even copying the items, you are copying the list, which means that you still only have one list, but two references to it.
To use the Clone method you have to call it for each item in the list:
rdNew.Hazards = rd.Hazards.Select(Function(x) x.Clone()).Cast(Of Hazard).ToList()
Imports System.IO
Imports System.Xml.Serialization
Public Function CopyList(Of T)(oldList As List(Of T)) As List(Of T)
'Serialize
Dim xmlString As String = ""
Dim string_writer As New StringWriter
Dim xml_serializer As New XmlSerializer(GetType(List(Of T)))
xml_serializer.Serialize(string_writer, oldList)
xmlString = string_writer.ToString()
'Deserialize
Dim string_reader As New StringReader(xmlString)
Dim newList As List(Of T)
newList = DirectCast(xml_serializer.Deserialize(string_reader), List(Of T))
string_reader.Close()
Return newList
End Function
I know this is old.
rdNew.Hazards = rd.Hazards.ToList()
Even though it's already a list, ToList will create a new list based on it.
With VB 2019, this will create a shallow copy, but that's useful in some circumstances. That means that the list is new, but the elements of both rd.Hazards and rdNew.Hazards point to the same thing.
If you edit any particular hazard, the changes will be seen in both.
If you add a hazard to one list, the other list will not have it.
If you delete a hazard from one list, the other list will still have it.
If Hazard were a primitive type (like a string or integer), then editing items would not be reflected in the other list.