How to use for loop to modify the object itself? - vb.net

I have a list of objects. Using a for loop I search for the object I want to modify and after finding my object I want to completely modify my object in tempMach.lstnVar. Below is my code:
For Each var As ListenVariable In tempMACH.lstnVar
If var.varName = newLstnVar.varName Then
var = DeepClone(newLstnVar)
Exit For
End If
Next
Deep clone function creates a deep clone of the newLstnVar. The loop assigns the value into var, but when the loop is done there are no change in tempMACH. I'm really confused how = operator works with objects. When I use = does it assign the reference or the value? When I declare a variable in for loop such as var in this case, is it just a variable with the same value in tempMACH.lstnVar, or is it a reference to it?

You can't use a For...Each if you want to replace the object itself since you can't assign anything to the loop variable. Instead use a For-loop:
For i As Int32 = 0 To tempMACH.lstnVar.Count() - 1
Dim obj As ListenVariable = tempMACH.lstnVar(i)
If obj.varName = newLstnVar.varName Then
tempMACH.lstnVar(i) = DeepClone(newLstnVar)
Exit For
End If
Next
You can use a For-loop only if the collection implements IList or IList(Of T), for example a List(Of String) or String(). Otherwise you have to re-create the sequence.

Related

VBA - Create object to redirect "Write" method

Right now I have an module where I call an object with the "write" method.
I do this 500 places around in the code, and now I need to support 2 objects instead of only 1 - But I will only write to one of them depending on an variable state
So originally I would have to write
If var = 1 Then
Module1.Obj1.Write("some_data")
Else If var = 0 Then
Module1.Obj2.Write("some_other_data")
End If
How do I create an object that can redirect the data based on an variable so I can write like:
Module1.ObjX.Write("data_here")
And down in the ObjX it reads "var" and if var = 1 then writes to Obj1 or if var = 0 write to Obj2
'if var = 1 then
Module1.Obj1.Write("data")
else if var = 0 then
Module1.Obj2.Write("data")
It would make the code easier to understand and save a lot of work :S
A standard way of doing what you want is to create a dictionary of key value pairs. The key is the value that selects which object you need to use to write and the value associated with the Key is the object you will use. The code below uses a scripting.dictionary to achieve this.
At the module Level
Public myWriter as Scripting.Dictionary
In an initialisation subroutine
Set myWriter = New Scripting.Dictionary
with myWriter
.Add 1,Obj1
.Add 0.Obj2
.Add 3.Obj3
' etc
End With
To call the approprite write function you can now just use
myWriter.Item(var).Write(DataValue)
'
Pass the object by reference into a write function instead:
Sub WriteData(ByRef obj, ByVal data)
'Write the data using the object here
End Sub
WriteData(Obj1, "data")
WriteData(Obj2, "data")
Change the value of writervar before you call write object
Since writervar is global you can edit the value anywhere in your code.
Public writervar as Integer ' global scope
Public Sub WriteObj(Data)
Select Case writervar
Case 0
' do stuff
Case 1
' do stuff
Case Else
' error
End Select
End Sub

Remove an object from a List(of) based on object value

How do I remove an object from a list based on value, I'me getting an index out of range error
'A class called Person exists'
Dim PersonOne As New Person
PersonOne.name = "John"
PersonOne.age = 50
'Created a list to store them'
Dim MyList As New List(Of Person)
MyList.Add(PersonOne)
'Now to remove an object'
Dim ObjectToRemove As String = "John"
For Each item in MyList
If String.Compare(item.name, ObjectToRemove) = 0 Then
'How do I get the index to remove?'
End If
Next
Excuse the code, I bashed it out off the top of my head, my original code is a bit more convoluted. But the gist is I just want to compare a value, if it matches remove that . object from the List(Of).
Use the FindIndex method which accepts a predicate delegate. You can pass a lambda function for the predicate delegate.
(Also, I think it's clearer to use String.Equals with an explicit StringComparison instead of using String.Compare(x,y) == 0 as it signals intent better).
Dim personOneHasThisIndex As Integer
personOneHasThisIndex = MyList.FindIndex( Function(p) p.name.Equals( ObjectToRemove, StringComparison.Ordinal ) )
If personOneHasThisIndx > -1 Then
' Always check if the result is -1, which means it doesn't exist
MyList.RemoveAt( personOneHasThisIndex )
End If
Note that List<T>.RemoveAt(Int32 index) is inefficient because it has to move every element in the List (lists cannot have "empty" spaces). If you have a conceptual list you'll be adding and removing from a lot, consider using LinkedList or a HashSet (if the order of the elements doesn't matter) instead.

Reference a variable using a string

I have a few variables called: _2sVal, _3sVal, _4sVal, etc
I want to change each of their values through a loop.
Like:
For i = 1 To 10
'set the value
Next
I've tried putting them in a dictionary like:
Dim varDict As New Dictionary(Of String, Integer)
varDict.Add("2sVal", _2sVal)
varDict.Add("3sVal", _3sVal)
varDict.Add("4sVal", _4sVal)
I can retrieve the value using
MsgBox(varDict(i.ToString & "sVal"))
But when I try to change it like
varDict(i.ToString & "sVal") = 5
It doesn't do anything. No errors or exceptions either, just the value stays unchanged
When you are using
varDict.Add("4sVal", _4sVal)
You are not putting the _4sVal variable inside the dictionary, but its value.
Then, changing the dictionary will not change the _4sVal, since there is no reference of it inside the dictionary.
What I mean is
varDict("4sVal") = 5
will change the value of dictionary but not the variable _4sVal itself.
I think the correct to do is define that variables as Properties, defined like:
Property _4sVal As Integer
Get
Return varDict("4sVal")
End Get
Set(value As Integer)
varDict("4sVal") = value
End Set
End Property
This way you will not have to change anything in the rest of your code. It will be transparent.

How Do I loop through this class once I have added items

How do i loop through this class once I add items via this method. Just I am quite new to generic lists so was wonding if someone could point me in right direction in datatables im used to doing the following:
For Each thisentry In dt.rows
Next
What do I use in collections
Calling Code
Calling this in my delciarations of main class
Dim infoNoProductAvail As List(Of infoProductsNotFound) = New List(Of infoProductsNotFound)()
this is how i am adding the files but I have checked in the routine and the count for the list is at 2 products
If medProductInfo.SKU.SKUID = 0 Then
infoNoProductAvail.Add(New infoProductsNotFound(thisenty2.Item("EAN13").ToString(), True))
End If
this is the class itselfs
Public Class infoProductsNotFound
Public Sub New(tbcode As String, notfound As Boolean)
Me.tagbarcode = tbcode
Me.notfound = notfound
End Sub
Private tagbarcode As String = String.Empty
Private notfound As Boolean
Public Property tbcode() As String
Get
Return tagbarcode
End Get
Set(ByVal value As String)
tagbarcode = value
End Set
End Property
Public Property isNotFound() As Boolean
Get
Return notfound
End Get
Set(ByVal value As Boolean)
notfound = value
End Set
End Property
End Class
Tried
I tried using the following
Function BuildExceptionsForEmail()
Dim retval As String = ""
Dim cnt As Int32 = 0
retval = "The following products are not avialable" & vbCrLf
For Each info As infoProductsNotFound In infoNoProductAvail
retval &= info.tbcode
cnt &= 1
Next
Return retval
but for some reason at this point my info noproductAvail is blank even though in the routine above its sitting at count of 2 what gives?
First I'd shrink that declaration a bit:
Dim infoNoProductAvail As New List(Of infoProductsNotFound)
Next, to iterate there are several options. First (and what you're likely most used to):
For Each info as infoProductsNotFound in infoNoProductAvail
If info.tbCode = "xyz" Then
DoSomething(info)
End If
Next
Or you might want to use lambda expressions (if you're using .Net 3.5 and above I think - might be .Net 4):
infoNoProductAvail.ForEach (Function(item) DoSomething(item))
Remember that generics are strongly typed (unlike the old VB collections) so no need to cast whatever comes out: you can access properties and methods directly.
If infoNoProductAvail(3).isNotFound Then
'Do something
End If
(Not that that is a great example, but you get the idea).
The For Each syntax is the same. It works the same way for all IEnumerable objects. The only "trick" to it is to make sure that your iterator variable is of the correct type, and also to make sure that you are iterating through the correct object.
In the case of the DataTable, you are iterating over it's Rows property. That property is an IEnumerable object containing a list of DataRow objects. Therefore, to iterate through it with For Each, you must use an iterator variable of type DataRow (or one of its base classes, such as Object).
To iterate through a generic List(Of T), the IEnumerable object is the List object itself. You don't need to go to one of it's properties. The type of the iterator needs to match the type of the items in the list:
For Each i As infoProductsNotFound In infoNoProductAvail
' ...
Next
Or:
Dim i As infoProductsNotFound
For Each i In infoNoProductAvail
' ...
Next
Or:
For Each i As Object In infoNoProductAvail
' ...
Next
Etc.

Values in list of structures don't change

I have defined a structure in my code and have a list of this structures"
Structure Parcel
Public name As String
Public type As String
End Structure
Dim ParcelList As New List(Of Parcel)
Then I'm trying to set some values to an element of the list which name is known to me
For Each myParcel As Parcel In ParcelList
If (myParcel.name = "Parcel1") Then
myParcel.type="Type1"
End If
Next
Unfortunately values in my list don't change at all. what am I doing wrong?
As Parcel is a Structure, it is passed by value so when iterating through collection, you are modifying a copy of your structure.
To better understand this case, you should understand what For Each really is. Your code can be translated into:
Dim enumerator As List(Of Parcel).Enumerator = ParcelList.GetEnumerator()
While enumerator.MoveNext()
' Here you have a local copy of your Structure
Dim myParcel As Parcel = enumerator.Current
Dim flag As Boolean = Operators.CompareString(myParcel.name, "Parcel1", False) = 0
If flag Then
' Here you modify your local copy
myParcel.type = "Type1"
End If
End While
If Parcel was a Class, it would be passed by reference so no local copy would be created and line myParcel.type = "Type1" would change proper object existing in your collection.
As already Stated this is because you are modifying a local copy of a value type. One way round this is to access the items in the list by ordinal and replace the ordinal value type with a new type:
For i As Integer = 0 To ParcelList.Count - 1
If ParcelList(i).name = "Parcel1" Then
ParcelList(i) = New Parcel With {.name = ParcelList(i).name, .type = "Type1"}
End If
Next
But really you should change the Sturcture to a Class
When checking for strings use Equals instead of '='.
If (myParcel.name.equals("Parcel1")) Then
myParcel.type="Type1"
End If
Strings are in fact 'Objects'. When you compare Strings (example StringA = StringB), you check the allocation of the String in Memory instead of the contents of the string.
Even better would be:
If (myParcel.name.ToUpper().equals(Cstr("Parcel1").toUpper())) Then
myParcel.type="Type1"
End If
That way you ignore any difference case-wise.
example:
myParcel.name = "teST"
myParcel.name.equals("test")
is False
myParcel.name.ToUpper().equals(Cstr("test").toUpper())
is true