Return the previous index in vb.net? - vb.net

I have a For Each loop that is looping through an array of strings to find the first string with a length of three characters. For example, when looping through the array, if the first 3 character string is the 4th index of the array, I would like for it to return the 3rd (previous) index of the array. Any help would be appreciated.
For Each piece As String In p
If piece.Length = 3 Then
'Return previous index
End If
Next

You have a couple of options.
If p is a collection accessible by index (ie: IList(Of T)), you can switch to a For loop, and return the element at the current index -1.
Otherwise, you can keep the previous item in a separate variable, and return it when your condition is met.
Dim lastItem as String
For Each piece As String In p
If piece.Length = 3 Then
Return lastItem ' Will return Nothing if this is the first item...
End If
lastItem = piece ' Store this for next time
Next

How about something like this:
For i As Integer = 1 To p.Count - 1
If p(i).Length = 3 Then
'Return previous index
Return p(i - 1)
End If
Next
Note the loop will have to start at 1 (not zero) otherwise you could return the (0-1)th element of the array which won't exist

I would use .FindIndex, if you have a List(Of T):
Dim i As Integer = p.FindIndex(Function(x) x.Length = 3)
If i > 0 Then
Dim previous As String = p(i - 1)
'Do something
End If
If you have a String(), you can also use the Array version:
Array.FindIndex(Of String)(p, Function(x) x.Length = 3)

Related

How to eliminate Undefined Behavior in this code

Function ChewQuery(QueryList As String)
'This function takes in a string and partitions it into a weight tree
Dim ElementList() As String
ElementList = QueryList.Split(";")
Dim LoadedWeight(ElementList.Length) As WeightElement
For x = 0 To ElementList.Length - 1
'Using this method, in which the front portion of the query is repeatedly removed, allows for a simpler query structure, so that we don't need to partition between the pointers and values
LoadedWeight(x).LowPointer = Int(BiteQuery(ElementList(x)))
LoadedWeight(x).HighPointer = Int(BiteQuery(ElementList(x)))
LoadedWeight(x).TraitPointer = Int(BiteQuery(ElementList(x)))
LoadedWeight(x).Num = Int(BiteQuery(ElementList(x)))
ChewQueryValues(ElementList(x), LoadedWeight(x))
Next
Return LoadedWeight
End Function
Function BiteQuery(ByRef QueryList As String)
Dim Marker As Integer
Dim Bite As String
'This function partitions the input string around the first comma
'It returns the section before the comma, and stores the section behind the comma as the new value for the string
Try
Marker = InStr(QueryList, ",")
Bite = Left(QueryList, Marker - 1)
Marker = Len(QueryList) - Marker
QueryList = Right(QueryList, Marker)
Catch
'This is used in the case that a list without a comma is input
Bite = QueryList
QueryList = ""
End Try
Return Bite
End Function
Sub ChewQueryValues(ByRef QueryList As String, ByRef LoadedWeight As WeightElement)
LoadedWeight.Values = {}
While Len(QueryList) > 0
'This While loop is so that an arbitrary number of values can be inserted
'Because BiteQuery takes in functions by reference, each loop reduces the length of the string until it is empty
ReDim Preserve LoadedWeight.Values(LoadedWeight.Values.Length + 1)
LoadedWeight.Values(LoadedWeight.Values.Length - 1).TraitName = BiteQuery(QueryList)
LoadedWeight.Values(LoadedWeight.Values.Length - 1).TraitNum = Int(BiteQuery(QueryList))
LoadedWeight.Values(LoadedWeight.Values.Length - 1).WeightValue = CDec(BiteQuery(QueryList))
End While
End Sub
This set of functions is exhibiting some sort of undefined/random behavior when run in the following case:
FullToken = Strings.Right(Strings.Left(Query, 10), 5) 'This set of functions will extract the 5 rightmost characters of the 10 leftmost characters. This is equivalent to the 5th to 10th characters, which is where the token is stored
QueryText = Strings.Right(Query, Len(Query) - 11)
ReDim Preserve Weight(Weight.Length + 1)
Weight(Weight.Length - 1).Token = FullToken 'This puts the token in the list
Weight(Weight.Length - 1).Weight = ChewQuery(QueryText) 'This puts the weight in the list
WeightList.Text += vbCrLf + FullToken 'This adds the token to the viewable label
This is causing other important parts of the program to fail, which is not desirable. How do I fix this code so that it performs identically on each run of the program?

Removing elements from list with condition (VB)

I have a list
Dim list As New List(Of Double)
I want to remove the last entries, if the differences are > 20.
My idea to check the last 30 entries:
Do While index >= list.Count - 30
If Math.Sqrt((list(index) - list(index + 1)) ^ 2) > 20 Then
list.RemoveAt(index)
Exit Do
End If
Loop
It does not lead to my solution. Can somebody help? Thank you very much.
Instead of using a List of doubles I would use a LinkedList of doubles to make use of the class methods and properties like AddFirst, Last, Last.Previous
So let's assume that you have a LinkedList declared like
Dim list As New LinkedList(Of Double)
And you have added elements to this list using
list.AddFirst(134.5678)
Now, you could remove from the end of the list with something like this
' You want to have a list of at least 30 elements
Do While list.Count > 30
' Last node and the previous one
Dim dLast = list.Last
Dim dLastPrev = list.Last.Previous
' Evaluate the two elements
If Math.Sqrt(dLastPrev.Value - dLast.Value) ^ 2 > 20 Then
' Remove the last and continue to evaluate the next pair
list.RemoveLast()
Else
' Stop if the condition is not met.
Exit Do
End If
Loop
Of course this could also be done using your current list type.
Also notice how I have swapped the two elements to verify. This is done to avoid any possibility of an IndexOutOfRangeException
Dim index as Integer = list.Count - 1
Do While index >= list.Count - 30
Dim prev = index - 1
If Math.Sqrt((list(prev) - list(index)) ^ 2) > 20 Then
list.RemoveAt(index)
else
Exit Do
End If
index = index - 1
Loop

For each loop, compare to next item on the list

I am writing an application where I bring change history of items from the database and place them on the table using For Each loop. I would, however, like to show in table what information has changed in each edit. Is it possible to compare variables of each item to the variables of next loop in For Each loop?
Something like:
For Each k As Examplemodel In Model
'Find next item on the loop after current one somehow
Dim nextItem = Model.Item(k+1) 'something like this
If k.ItemsName <> nextItem.Itemsname 'if the name has changed in edit
'show result in bold
Else
'show result in normal font weight
End If
Next
Is this possible and if not, what's the best way to achieve this?
You can't do it in a foreach loop directly.
If your Model class have indexers you can easily convert it into a for loop:
If Model.Count > 1 Then
For i as Integer = 0 to Model.Count - 2 ' Note the -2 here !!!
Dim Item As Examplemodel = Model(i)
Dim NextItem As Examplemodel = Model(i + 1)
if Item.ItemsName <> NextItem.ItemsName then
'show result in bold
else
'show result in normal font weight
end if
Next
'show result of NextItem here, since the last item doesn't get shown in the loop
Else
'show result of only item here
End If
If not, you can use a workaround like this:
Dim PrevItem as Examplemodel = Nothing ' Assuming a reference type
For Each k As Examplemodel In Model
If Not IsNothing(PrevItem) AndAlso k.ItemsName <> Prev.Itemsname 'if the name has changed in edit
'show result (of PrevItem!!!) in bold
Else
'show result (of PrevItem!!!) in normal font weight
End If
PrevItem = k
Next
'show result (of PrevItem (currently the last item in Model) in normal font weight
You should use a normal for loop:
Dim numbers() As Integer = New Integer() {1, 2, 4, 8}
Sub Main()
For index As Integer = 0 To numbers.Length - 2
Dim currentInt As Integer = numbers(index)
Dim nextInt As Integer = numbers(index + 1)
Console.WriteLine(currentInt)
Console.WriteLine(nextInt)
Next
End Sub
Another approach to use LINQ Aggregate extension method, which use first item of collection as initial value. So every item will have access to previous one.
Public Class ItemChanges
Public Property Item As ExampleModel
Public Property Changes As New Hash(Of String)
End Class
Public Function Check(previous As ItemChanges, current As ItemChanges) As ItemChanges
If current.Item.Name <> previous.Item.Name Then
current.Changes.Add(Nameof(current.Name))
End
Return current
End Function
' assume model is collection of items
Dim itemWithChanges =
model.Select(Function(m) New ItemChanges With { .Item = m })
.Aggregate(AddressOf(Check))
.ToList()
Then you can use calculated result as you want - every item will have a hash of property names which had changed
If checkedItem.Changes.Contains(Nameof(checkedItem.Item.Name)) Then
' use bold font or different color
End

How do you delete empty lines from a textfile

I am trying to delete empty lines from a textfile using vb.net. This is an example of what I have tried so far however it is not working as expected:
Dim i As Integer = 0
Dim CountDeleted = 0
Dim TextString As String
For Each Line As String In lines
TextString = lines(i)
If TextString.Trim = "" Then
lines.RemoveAt(i)
CountDeleted += 1
End If
i += 1
Next Line
This is an example of the data within a textfile that I would like to remove the lines from:
BUSINESS_UNIT|PO_ID|LINE_NBR|CANCEL_STATUS|
A
B
C
Required output:
BUSINESS_UNIT|PO_ID|LINE_NBR|CANCEL_STATUS|
A
B
C
Any help would be much appreciated
To remove all the blank lines is just one line of code with linq
Dim nonBlank = lines.Where(Function(x) Not String.IsNullOrWhiteSpace(x))
Counting the removed is just a difference between elements in the two lists
Dim CountDeleted = lines.Count - nonBlank.Count
Your code will trigger a runtime exception because you are removing an item from the same collection that you enumerate with the For Each loop.
You could switch to an old fashioned For Next loop but be careful to start from the end of the collection and examine the strings toward the beginning of the collection.
For i = lines.Count - 1 To 0 Step - 1
TextString = lines(i)
If string.IsNullOrWhiteSpace() Then
lines.RemoveAt(i)
CountDeleted += 1
End If
Next
This backward loop is required because when you remove an item from the collection the total items count decrease and the items following the current index will slide one position. A normal (forward) loop will skip to examine the item following the one deleted.
To remove all WhiteSpace strings from List(Of String):
lines.RemoveAll(addressOf String.IsNullOrWhiteSpace)
To remove all WhiteSpace lines from a text file:
File.WriteAllText(path, Regex.Replace(File.ReadAllText(path), "(?m)^\s+^", ""))

String.contains not working

I'm trying to filter a list based on input from a textbox. If the item doesn't contain the string, it is deleted from the list. Here is my subroutine:
Sub filterlists(filter As String)
Dim removalDifferential As Integer = 0
For colE As Integer = 0 To RadListView1.Items.Count
Try
Dim itemEpp As ListViewDataItem = Me.RadListView1.Items(colE)
Dim jobname As String = itemEpp(0)
If Not jobname.Contains(filter) Then
' MsgBox(jobname & " Contains " & filter)
RadListView1.Items.RemoveAt(colE - removalDifferential)
removalDifferential = removalDifferential + 1
End If
Catch
End Try
Next
End Sub
Currently this is not deleting the correct items. The TRY is there because when you delete an item the list index changes (which means the for loop length is wrong and will throw outofbounce errors). Any other loop options that will work here?
Assuming you really do want to delete any LVI which simply contains the filter text, you should loop backwards thru the items (any items, not just Listview items) so the index variable will in fact point to the next correct item after a deletion:
For n As Integer = RadListView1.Items.Count-1 to 0 Step -1
If radListView1.Items(n).Text.Contains(filter) Then
RadListView1.Items.RemoveAt(n)
End If
Next