String split in vb.net - arrays

I'm new to Visual Basic .net and I would like some help doing this:
I'm receiving these strings:
!re=.id=*10000AF=name=Down-PBX=parent=Down=packet-mark=pack_pbx=limit-at=256000=queue=PCQ_Down=priority=1=max-limit=512000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=false=disabled=true=comment=PBX
!re=.id=*10000B0=name=Up-PBX=parent=Up=packet-mark=pack_pbx=limit-at=256000=queue=PCQ_Up=priority=1=max-limit=512000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=true=disabled=true=comment=PBX
!re=.id=*10000C7=name=Down_Mauro=parent=Down=packet-mark==limit-at=315000=priority=8=max-limit=5000000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=false=disabled=true
(Only pasted 3 but there are many more)
And I have them in a String Array called tree().
What I want to do is a function that returns new Array, I guess a multidimensional one, that separates everything between the "=", starting from "name". Like this:
Down-PBX, Down, pack_pbx, 256000, PCQ-DOWN, etc
Up-PBX, UP, pack_pbx, 256000, etc
And so on.
Any help wil be really appreciated!! Thanks.

Assuming that the first value you want is always the 5th member of the array, and you wish to return every 2nd value in the array after that:
Private Function GetParts(source As String) As String()
Dim Parts As String() = source.Split(New Char() {"="C})
Dim Items As New List(Of String)()
For i As Integer = 4 To Parts.Length - 1 Step 2
Items.Add(Parts(i))
Next
Return Items.ToArray()
End Function
Essentially, you start at the 5th part of of the split string, and then iterate every 2nd element after that, adding each one to a generic list of strings. You then return this as an array of string.
I do not have a copy of VB here so it may not work 100%, but it should give you the general gist of what you are trying to achieve. I wrote it in C# and then attempted to convert it to VB.Net (fingers crossed!).

Quite easy:
Dim output = input.Substring(input.IndexOf("name=") + 5) _
.Split("="c) _
.Where(Function(e, i) i Mod 2 = 0) _
.ToArray()
output content:
Down-PBX
Down
pack_pbx
256000
PCQ_Down
1
512000
0
0
00:00:00
false
true
PBX
Multidimensional version:
Dim input() As String = {
"!re=.id=*10000AF=name=Down-PBX=parent=Down=packet-mark=pack_pbx=limit-at=256000=queue=PCQ_Down=priority=1=max-limit=512000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=false=disabled=true=comment=PBX",
"!re=.id=*10000B0=name=Up-PBX=parent=Up=packet-mark=pack_pbx=limit-at=256000=queue=PCQ_Up=priority=1=max-limit=512000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=true=disabled=true=comment=PBX",
"!re=.id=*10000C7=name=Down_Mauro=parent=Down=packet-mark==limit-at=315000=priority=8=max-limit=5000000=burst-limit=0=burst-threshold=0=burst-time=00:00:00=invalid=false=disabled=true"
}
Dim output = input.Select(Function(i) i.Substring(i.IndexOf("name=") + 5) _
.Split("="c) _
.Where(Function(e, idx) idx Mod 2 = 0) _
.ToArray()) _
.ToArray()
output is String()() here.

Related

VBA user-defined-datatype array to range

I have a public variant array populated with a range containing several data types (dates, numbers, strings, booleans...)
I have functions taking those types as parameter and works on the "columns" of my array.
Each function have been tested by passing them directly variable (not arrays) and worked fine.
But when the program tries to compile, it returns me an error : "argument Type Byref not compatible". To me, I don't declare things how i should. Any advice would be welcome.
Dim myArray() As Variant
myArray = importedRange.Value
myArray (12) = aFunction(myArray (3))
Edit :
I the public variant array is no more. I replaced it with an array of a new private type composed of the types I needed. It works perfectly.
In order to fill this array I copy data from ranges using "For" loops (for a test purpose but i'm open to any upgrade).
I wonder now if there is a way to copy my new array in a given range avoiding using more loops.
This line worked for my first variant array but doesent with the new one.
Dim zoneStockage As Range
Set zoneStockage = Sheets("Archive_Test").Range("A1")
Dim tableauFactures(3) As Facture
'allocating my new array.
(I've skipped this code because there are 18 parameters composing it.)
zoneStockage.Resize(UBound(tableauFactures, 1), 18).Value = tableauFactures
Thanks for your help.
It's ugly, maybe not more effective than using "sheets.range" but it works anyway:
For i = 0 To 5
With myUDT(i)
.Name = Sheets("a").Range("A" & i)
.Color = Sheets("a").Range("B" & i)
.dateCreation = Sheets("a").Range("C" & i)
.dateDebutContrat = Sheets("a").Range("D" & i)
End With
Next i

compare two arrays and copy unique element from second to first array VB.NET

I have two arrays: TempSubject(), which carries a list of subjects, and friRoutine(), which indicates when subjects are taught.
I need to enforce a rule that no particular subject can not be taught twice on a specific day. I have a function named DetectDuplicate(friRoutine, Tempsubject(i)), which checks friRoutine() with TempSubject() and finds whether friRoutine already contains a particular subject. If No, then add that unique subject to friRoutine. Otherwise continue through the loop for next subject. My code is furnished below as follows-
Dim TempSubject() As String = {"Maths","English","Sanskrit","Sanskrit","Urdu","Urdu","Urdu","French","French","French","Physical Education","Physical Education","Game","Game", "Science"}
dim c4 as integer = 0
dim limitcounter = 0
dim friRoutine() as string
reedim friRoutine(6)
While c4 < TempSubject.Length
If DetectDuplicate(friRoutine, TempSubject(c4)) = False And limitcounter4 < 7 Then
friRoutine(limitcounter4) = TempSubject(c4)
'MessageBox.Show(c1 & ": " & limitcounter1 & ": " & tueRoutine(limitcounter1))
Debug.WriteLine("Friday: " & c4 & ": " & limitcounter4 & ": " & friRoutine(limitcounter4))
TempSubject.RemoveAt(c4)
limitcounter4 = limitcounter4 + 1
End If
c4 = c4 + 1
End While
Private Function DetectDuplicate(ByVal arr As Array, ByVal str As String) As Boolean
Dim numcount As Integer = 0
For numcount = 0 To arr.Length - 1
If arr(numcount) = str Then
Return True
End If
Next
Return False
End Function
The Output is supposed to be
friRoutine = Math, english, sanskrit, Urdu, French, Physical education, game
but unfortunately, English is missing.
the output is:
friRoutine = Math, sanskrit, Urdu, French, Physical education, game, science
I'm afraid of using my code as it may spoil the entire process at any point of time.
I think the problem is with Function DetectDuplicate()
Edit (From a comment)
I have to remove the used elements. My original program will have 42 subjects and 6 days. 7 periods everyday. For example if i picked up 7 unique subjects for Monday then for the next 6 days i must have only 35 subjects to be adjusted for rest of the 5 days.
The first thing I would do is convert the arrays to generic Lists. So this:
Dim TempSubject() As String = {"Maths","English","Sanskrit","Sanskrit","Urdu","Urdu","Urdu","French","French","French","Physical Education","Physical Education","Game","Game", "Science"}
Dim friRoutine() As String
Becomes this:
Dim TempSubject As New List(Of String) From {"Maths","English","Sanskrit","Sanskrit","Urdu","Urdu","Urdu","French","French","French","Physical Education","Physical Education","Game","Game", "Science"}
Dim friRoutine As New List(Of String)(6)
You can access items in these lists by index, just like with arrays, but now it becomes MUCH easier to do things like add or remove entries. At least, I would do this for the friRoutine collection; there is some argument to made for an array in way TempSubject is used in this code, but even there the data likely came form somewhere that would justify a List(Of String) instead.
That done, you can greatly simplify the code:
friRoutine.AddRange(TempSubject)
friRoutine = friRoutine.Distinct().Take(7).ToList()
Or, to show the entire sample:
Dim TempSubject = {"Maths","English","Sanskrit","Sanskrit","Urdu","Urdu","Urdu","French","French","French","Physical Education","Physical Education","Game","Game", "Science"}
Dim friRoutine As New List(Of String)(TempSubject.Distinct().Take(7))
For i As Integer = 0 to friRoutine.Count - 1
Debug.WriteLine($"Friday: {i} : {friRoutine(i)}")
Next
And now that we see the data for friRoutine can be computed from a single line of code, one wonders if we really need the separate collection at all.
As for the original code, the big problem was this line:
TempSubject.RemoveAt(c4)
This modified the collection while still looping through it. Everything would shift forward, causing some items to be skipped.
Just use Enumerable.Distinct<T>()
Dim TempSubject = {"Maths","English","Sanskrit","Sanskrit","Urdu","Urdu","Urdu","French","French","French","Physical Education","Physical Education","Game","Game", "Science"}
Dim friRoutine = TempSubject.Distinct().ToArray()
If you want a List output instead, then it's simply
Dim friRoutine = TempSubject.Distinct().ToList()
Since both array and List are Enumerable, TempSubject can be either an array or List.

VB Convert 2 DataRows to a Single String in an Array

My Goal is to take two rows(FirstName and Surname) Convert them to a single Array of "FirstName, Surname".
This is my terrible code i eventually put together
Private Sub Search_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'DbaPatientDataSet.tblPatientData' table. You can move, or remove it, as needed.
Me.TblPatientDataTableAdapter.Fill(Me.DbaPatientDataSet.tblPatientData)
listFirst.DataSource = Me.TblPatientDataBindingSource
listFirst.DisplayMember = "FirstName"
listLast.DataSource = Me.TblPatientDataBindingSource
listLast.DisplayMember = "Surname"
Dim Lenth As Integer = Me.listFirst.Items.Count - 1
Dim count As Integer = 1
Dim ArrFirst(Lenth) As String
Dim ArrLast(Lenth) As String
For count = 1 To Lenth
ArrFirst(count) = listFirst.Items(count).ToString
ArrLast(count) = listLast.Items(count).ToString
Next count
count = 1
For count = 1 To Lenth
arrFullName(count) = ArrLast(count) & ", " & ArrFirst(count)
Next count
'Arrays Set =====================================================
But with this code i get an Array of
`"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
"Sytem.Data.DataRowView, Sytem.Data.DataRowView"
`
As you can see
Here
There must be an easy way to convert both DataRows to strings then concatenate them together in an array
I am going to search this array using a Binary Search to find a desired name
Thanks
First, I think you are confusing your rows and your columns. You have 2 columns. I went directly to full name but I think you can break it out if you need to.
Dim arrNames(ListBox1.Items.Count - 1) As String
For i As Integer = 0 To ListBox1.Items.Count - 1
arrNames(i) = $"{ListBox1.Items(i)} {ListBox2.Items(i)}"
Next
For Each item In arrNames
Debug.Print(item)
Next
The string with the $ in front is an interpolated string. Sort of an improvement to String.Format.
I know there is an answer but for now you could go direct to the data table to get what you need.
Dim arrNames(ListBox1.Items.Count - 1) As String
Dim i As Integer = 0
Dim dt As DataTable = DbaPatientDataSet.Tables(0)
For Each row As DataRow In dt.Rows
arrNames(i) = $"{row("Surname")}, {row("FirstName")}"
i += 1
Next
For Each item In arrNames
Debug.Print(item)
Next
'assume the names of your columns are Surname and FirstName
If I run your code up, I get the result you are looking for, so I'm not sure what you are missing. In saying that though, you are making things hard on yourself by messing around with arrays :). Just use the dataset rows directly - they are strongly typed and you can check for nulls etc as needed... something like this;
Dim fullNames As New List(Of String) '-- or you could fill your array.
For Each row As DbaPatientDataSet.tblPatientDataRow In ds.tblPatientData
fullNames.Add(row.Surname & ", " & row.FirstName)
Next
Just looking at what you are trying to achieve, if it was me, I would be bringing back the formatted data in my query that fills the dataset i.e. a third, FullName, column.
It has been in the back of my mind. Finally got it for the List Box directly.
Dim arrFullNames(ListBox1.Items.Count - 1) As String
Dim i As Integer = 0
For Each item As DataRowView In ListBox1.Items
arrFullNames(i) = $"{DirectCast(item("Surname"), String)}, {DirectCast(item("Firstname"), String)}"
i += 1
Next
For Each item As String In arrFullNames
Debug.Print(item)
Next

Conditionial Concatenation in VBA

Short disclaimer: This is my very first question, so please be understanding.
I'm trying to code a function in Excel VBA that takes a binary number (formatted as a string) that counts the spaces, the 0s, and outputs the consecutive amount of 0s (and adds one to it) until the next "1" in that string appears, with "," in between. At the very end of the string, if there's the a "1", the output should look like: "..., 1". I'll try to make a little easier to understand with an example:
Strings of length twelve:
101011010101 --> 2, 2, 1, 2, 2, 2, 1
110000101101 --> 1, 5, 2, 1, 2, 1
100010001000 --> 4, 4, 4
So far I've managed to make a one-dimensional boolean array that takes the string and splits it into parts of length 1, so to say a "binary array". But I couldn't test it, since I only have half of the function.
Function ABSTAND(str As String, size As Integer) As String
Dim i As Integer
Dim arrays(size) As Boolean
For i = 1 To Len(str)
If Mid(str, i, 1) = "1" Then
arrays(i) = True
Else
arrays(i) = False
End If
'Maybe all of this works in a loop?
'Count consecutive 0s, add one
'Output the value with ", " (Concatenate numbers with strings, or make the number a string)
'Count consecutive 0s again
'and so on...
'Add conditions for the last value: Don't add a ", " and check if a one is the last character
End Function
I know, it looks like I have already figured out the problem and only need to implement it, but I'm new to VBA and don't know that well how it works and how to fix syntax and other problems.
Also, I would like to have a generalized form of my problem, that works with every string length, if that's possible.
Unfortunately, VBA isn't the best language for dealing with arrays. I've written the function as described but it sounds like, from your description, you might want to remove the check for If Len(arrZeroes(i)) > 0 Then etc. because it looks like you want to return '1' values in those cases.
Have a play around with the 'Split' function to try get a better feel for it and how it relates to your problem.
Function ABSTAND(str As String) As String
Dim arrZeroes() As String
Dim arrResult() As String
Dim i As Integer
'Initialise arrResult dimensions
ReDim arrResult(1 To 1)
'Splits your binary number into an array with each element being zero or more 0's
'(if there are several 1's in a row or a 1 at the start or finish it returns 0-length element for that position)
arrZeroes = Split(str, "1")
'Loop through each element in this new array
For i = LBound(arrZeroes) To UBound(arrZeroes)
'Sets the top element of the result array to this length + 1 then increments the size (like appending to array)
arrResult(UBound(arrResult)) = Len(arrZeroes(i)) + 1
ReDim Preserve arrResult(1 To UBound(arrResult) + 1)
Next i
'The last step on the result array incremented its ubound which is still empty so we remove that
ReDim Preserve arrResult(1 To UBound(arrResult) - 1)
'then return the array joined!
ABSTAND = Join(arrResult, ", ")
End Function
Let me know if anything doesn't make sense!
Edit:
An array is basically like an indexed list of elements of a specified data type. The line Dim arrZeroes() as String is dimming a dynamic array of string elements. The benefit of using a dynamic array is that you can dynamically change the amount of elements in it using Redim. To get around the lack of an 'append' function, what you have to do is redim the array to make it one element bigger then set this new biggest element to your new value.
arrResult(UBound(arrResult)) = Len(arrZeroes(i)) + 1
ReDim Preserve arrResult(1 To UBound(arrResult) + 1)
That's what these lines are doing. What the Split function does is, given a string and a delimiter, cut the string into a load of slices at each occurrence of the delimiter and return the array. For example, Split("11101101", "0") returns {"111", "11", "1"}. Where there are more than one occurrence of the delimiter in sequence, or the delimiter appears at the beginning or end of the string, it outputs a zero-length element, such as Split("101101", "1") returns {, "0", , "0",}. What the function is doing is looking at the length of each of these slices (i.e. consecutive digits that are not the delimiter) and outputting them to a new array.
Remove the # in your code.
It is used for preprocessing directive, and you do not need it.
More about Preprocessing directives:
https://msdn.microsoft.com/VBA/Language-Reference-VBA/articles/ifthenelse-directive
In general, try the following code:
Option Explicit
Public Sub TestMe()
Debug.Print ABSTAND("101011010101", 12)
End Sub
Function ABSTAND(str As String, size As Long) As String
Dim i As Long
Dim arrays() As Variant
Dim strResult As String
ReDim arrays(size)
For i = 1 To Len(str)
If Mid(str, i, 1) = "1" Then
arrays(i) = True
Else
arrays(i) = False
End If
strResult = strResult & arrays(i)
Next i
ABSTAND = strResult
End Function
It would print something like TrueFalseTrueFalseTrueTrue... Then try to build up your solution further.
Just some points:
Pay attention how the Array is created.
Use Long instead of Integer.
You can shorten the ABSTAND function, to the following:
Function ABSTAND(str As String, size As Long) As String
Dim i As Long
Dim arrays() As Variant
Dim strResult As String
ReDim arrays(size)
For i = 1 To Len(str)
arrays(i) = Mid(str, i, 1) = "1"
strResult = strResult & arrays(i)
Next i
ABSTAND = strResult
End Function

How to print 3 letter words from a list which were taken from a split array VB

I'm trying to print the contents of a list consisting of 3 letter words from an array. The code works but it will print the list multiple times and I am unsure as to why. Can anyone help? i'm building a decryption software using frequency analysis and this step is a crucial part according to my teacher. I'm using a windows form application in visual studio. Below is the code and the result of it.
Private Sub threeLetterWordButton_Click(sender As Object, e As EventArgs) Handles threeLetterWordButton.Click
freqTextBox.Show()
Dim threeWordList As New List(Of String)
Dim encryptedText As String = encryptionInput.Text
Dim encryptedArrary() As String = Split(encryptedText)
For Each item In encryptedArrary
For i = 0 To encryptedArrary.GetUpperBound(0)
If encryptedArrary(i).Length = 3 Then
threeWordList.Add(encryptedArrary(i))
For Each j In threeWordList
freqTextBox.Text = freqTextBox.Text & j & " "
Next
End If
Next
Next
End Sub
End Class
Here is the result of the code
You don't need the 3 loops. This code will do it with one:
For Each item In encryptedArrary
If item.Length = 3 Then
threeWordList.Add(item)
freqTextBox.Text = freqTextBox.Text & item & " "
End If
Next
Extending the previous answer, you could reduce the code checking for the length directly into the loop declaration, like this:
For Each item In encryptedArrary.Where(Function(x) x.Length = 3)
threeWordList.Add(item)
freqTextBox.Text = freqTextBox.Text & item & " "
Next
That way, you extract the 3-length items previously the loop start, reducing the number of iterations

Resources