Populate list box from string on different lines - vb.net

I have a query that returns a list of items that are in one listbox but not another. I want to put the results of that into a different listbox, problem is that when I try to put the results into a listbox, it puts them all on the same line. Say it returns three results (project_green Project_blue_ project_red), its adding them to one line in the list box as (project_greenproject_blueproject_red). I want each item to be on its own line but I can't get it there.
Here is the code I have
Dim result As List(Of String) = (From s1 As String In Me.ListBox1.Items Where Not Me.ListBox4.Items.Contains(s1) Select s1).ToList()
ListBox5.Items.Add(String.Join(Environment.NewLine, result))
Any help would be appreciated.
Thanks

Dim result = From s1 As String In ListBox1.Items Where Not ListBox4.Items.Contains(s1) Select s1
ListBox5.Items.AddRange(result)
This also saves you a bunch of extra memory. There's no need to allocate a List when the IEnumerable result form the linq query will already do on its own.
Though personally, I'm not a fan of the query comprehension syntax and prefer to write it this way:
Dim result = ListBox1.Items.Where(Function(s1) Not ListBox4.Items.Contains(s1))
ListBox5.Items.AddRange(result)

Related

Refined list sorting by substring integer after alphabetical sorting

I have some information in a list (called listLines). Each line below is in a List(Of String).
1|This is just a header
3|This is just a footer
2|3456789|0000000|12312312313|BLUE|1|35.00
2|7891230|0000000|45645645655|BLUE|1|22.00
2|7891230|0000000|45645645658|RED|2|13.00
2|3456789|0000000|12312312316|RED|2|45.00
2|3456789|0000000|12312312317|YELLOW|5|-9.00
2|3456789|0000000|12312312315|ORANGE|3|15.00
2|7891230|0000000|45645645659|YELLOW|5|32.00
2|3456789|0000000|12312312314|GREEN|4|-20.00
2|7891230|0000000|45645645656|GREEN|4|39.00
2|7891230|0000000|45645645657|ORANGE|3|-18.50
I'm doing a listLines.sort() on the list to sort it alphabetically. Below is what I get after the .sort().
1|This is just a header
2|3456789|0000000|12312312313|BLUE|1|35.00
2|3456789|0000000|12312312314|GREEN|4|-20.00
2|3456789|0000000|12312312315|ORANGE|3|15.00
2|3456789|0000000|12312312316|RED|2|45.00
2|3456789|0000000|12312312317|YELLOW|5|-9.00
2|7891230|0000000|45645645655|BLUE|1|22.00
2|7891230|0000000|45645645656|GREEN|4|39.00
2|7891230|0000000|45645645657|ORANGE|3|-18.50
2|7891230|0000000|45645645658|RED|2|13.00
2|7891230|0000000|45645645659|YELLOW|5|32.00
3|This is just a footer
With that said, I need to output this information to a file. I'm able to do this ok. I still have a problem though. There is a sequence number in the above data at position 5 just after the listed colors (RED, BLUE, ETC..) that you can see. It's just before the last value which is a decimal type.
I need to further sort this list, keeping it in alphabetical order since position 2 is an account number and I want to keep the account numbers grouped together. I just want them to be resorted in sequential order based on the sequence number.
I was looking at another thread trying to figure out how I can do this. I found a piece of code like listLines.OrderBy(Function(q) q.Substring(35)).ToArray. I think this would probably help me if this was a fixed length file, it isn't however. I was thinking I can do some kind of .split() to get the 5th piece of information and sort it but then it's going to unalphabetize and mix the lines back up because I don't know how to specify to still keep it alphabetical.
Right now I'm outputting my alphabetical list like below so I can format it with commas and double quotes.
For Each listLine As String In listLines
strPosition = Split(listLine, "|")
Dim i As Integer = 1
Dim iBound As Integer = UBound(strPosition)
Do While (i <= iBound)
strOutputText = strOutputText & Chr(34) & strPosition(i) & Chr(34) & ","
i += 1
Loop
My main question is how do I re-sort after .sort() to then get each account (position1) in sequential order (position 5)? OR EVEN BETTER, how can I do both at the same time?
The List(Of T) class has an overload of the Sort method that takes a Comparison(Of T) delegate. I would suggest that you use that. It allows you to write a method or lambda expression that will take two items and compare them any way you want. In this case, you could do that like this:
Dim items = New List(Of String) From {"1|This Is just a header",
"3|This Is just a footer",
"2|3456789|0000000|12312312313|BLUE|1|35.00",
"2|7891230|0000000|45645645655|BLUE|1|22.00",
"2|7891230|0000000|45645645658|RED|2|13.00",
"2|3456789|0000000|12312312316|RED|2|45.00",
"2|3456789|0000000|12312312317|YELLOW|5|-9.00",
"2|3456789|0000000|12312312315|ORANGE|3|15.00",
"2|7891230|0000000|45645645659|YELLOW|5|32.00",
"2|3456789|0000000|12312312314|GREEN|4|-20.00",
"2|7891230|0000000|45645645656|GREEN|4|39.00",
"2|7891230|0000000|45645645657|ORANGE|3|-18.50"}
items.Sort(Function(x, y)
Dim xParts = x.Split("|"c)
Dim yParts = y.Split("|"c)
'Compare by the first column first.
Dim result = xParts(0).CompareTo(yParts(0))
If result = 0 Then
'Compare by the second column next.
result = xParts(1).CompareTo(yParts(1))
End If
If result = 0 Then
'Compare by the sixth column last.
result = xParts(5).CompareTo(yParts(5))
End If
Return result
End Function)
For Each item In items
Console.WriteLine(item)
Next
If you prefer a named method then do this:
Private Function CompareItems(x As String, y As String) As Integer
Dim xParts = x.Split("|"c)
Dim yParts = y.Split("|"c)
'Compare by the first column first.
Dim result = xParts(0).CompareTo(yParts(0))
If result = 0 Then
'Compare by the second column next.
result = xParts(1).CompareTo(yParts(1))
End If
If result = 0 Then
'Compare by the sixth column last.
result = xParts(5).CompareTo(yParts(5))
End If
Return result
End Function
and this:
items.Sort(AddressOf CompareItems)
Just note that this is rather inefficient because it splits both items on each comparison. That's not a big deal for a small list but, if there were a lot of items, it would be better to split each item once and then sort based on those results.

How to pass Value from Last Loop?

I have been attempting many ways to retrieve 2 collections together, while the first collection holds a comma-separated value in a column, we can not find a solution to passing the first collection value to the second For Each.
This code simply retrieves database rows and adds each result to our list control using the Add() method.
Dim transferstable As New DataTable
count = 0
For Each row As DataRow In transferstable.Rows
Dim name = Truncate(row.Item("name"), 42)
ListControl1.Add(name, row.Item("username")", row.Item("added"), avatars, row.Item("online"), images(count), 0)
count += 1
Next
Problem
We need to nest the loops, so we get the value from the first collection from the "avatars" column (image1,image2,image3) and call it from Add() - 4th parameter.
We only get always 1 string result into the view, while the actual query reports many rows with 2 strings (image1, image2) so I tried this:
Dim lst As New List(Of String) From {
transferstable.Rows(0).Item(8)
}
count = 0
For Each item As String In lst
For Each row As DataRow In transferstable.Rows
Dim name = Truncate(row.Item("name"), 42)
ListControl1.Add(name, row.Item("username")", row.Item("added"), item, row.Item("online"), images(count), 0)
count += 1
Next
Next
And the still the same single result! (8) is the GROUP_CONCAT column for "avatars" How do we pass this list over to the 4th parameter?
We want to retrieve these as URL remote images and render them to view with Bitmap.
Expected result:
A list of 15x15 pictures that represent each split result from GROUP_CONCAT(avatars)
I've been at all different ways to do this for most of the day, I know nesting is the right direction but I can't figure out why only 1 result is coming back (image1 - not image1,image2, etc.)
Some physical image files do not exist anymore, so rendering that to view also has it halt after a few single string results, so it quits and gives an error, like a 404 but does not proceed with the 180 other rows.
https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/for-each-next-statement

Storing field names from a query table into a dynamic array MS Access

I have a Query qryRuleSets which outputs a table with 19 fields (that I do not want to save into an access table before that is suggested). I would like to get the field names and store them into an array so I can use that array in a for loop later on.
To find the number of fields that in the query result (to use in for loop later on) I have implemented the following, where the number of fields is stored in the variable numberfields -
numberfields = CurrentDb.QueryDefs("qryrulesets").Fields.Count
To actually get the name of these fields and store them in an array I am running into 2 problems:
1. Getting the field names from the query result
2. Setting up a dynamic array so that if the query ends up returning a table with more or less than 19 fields, it will still work
For my first problem:
I have tried to follow the steps in the following link: Get Column name from a query table but I can't figure it out.
To get the field names from the qry result I have tried the following but I'm not overly knowledgeable in vba/access so finding it hard to understand, even after a whole lot of googling:
Dim qry As QueryDef
Dim fieldNames As QueryDef
Dim firstcol As String
Set fieldNames = CurrentDb.CreateQueryDef(qry.qryrulesets)
firstcol = fieldNames.field(0).Name
For my second problem:
To store values in an array I have tried the following (as a test) and it works but I have to define the size of the array. Is there a way where it can be dynamic, i.e based on the value of the number of fields (found above stored in numberfields) :
Dim vardata(30) As Variant
For i = 1 To numberfields
vardata(i) = "hello"
Next i
I tried making the '30' above to a variable value but it didn't like that.
Any and all help will be appreciated. Thanks!
You can do like this:
Public Function GetFieldNames(ByVal QueryName As String) As String()
Dim Query As DAO.QueryDef
Dim FieldNames() As String
Dim Index As Integer
Set Query = CurrentDb.QueryDefs(QueryName)
ReDim FieldNames(0 To Query.Fields.Count - 1)
For Index = LBound(FieldNames) To UBound(FieldNames)
FieldNames(Index) = Query.Fields(Index).Name
Next
GetFieldNames = FieldNames()
End Function

Remove A Row From A List Of DataRow

I am using a Dictionary to create a key to a List Of DataRow. I want to iterate through each key and remove rows in the List. This will throw an out of range exception when I explicitly try to remove a row. How can I alter my code to accomplish this?
For Each g As KeyValuePair(Of [String], List(Of DataRow)) In grouped
For Each row As DataRow In g.Value
If CInt(segment(1)) <= 4 Then
'THIS THROWS AN OUT OF RANGE EXCEPTION
g.Value.Remove(row)
End If
Next
Next
I only want to remove specific rows based on criteria. Can someone post an example? I am on an old browser the "add comment" function does not work
Can you show a code example of how to use a predicate based on row.Item("ID") with the RemoveAll function?
I tried this and am getting an exception
g.Value.RemoveAll(Function(l) l.Item(Con.ID) Is l.Item(Con.ID).ToString)
Use List.RemoveAll. Not only will this make the act of removing all of the items easier than trying to remove items in some form of looping construct, but it will be dramatically more efficient as the List can reorganize all of the items once at the end, rather than moving the items down one index at a time over and over.
I figured it out using a reverse For loop. I did not see an examlpe on how to use the RemoveAll. Please post an example if you have time
For i As Integer = g.Value.Count - 1 To 0 Step -1
Dim row As DataRow = CType(g.Value(i), DataRow)
Dim segment() As String = row.Item(c._ID).ToString.Split("-"c)
If CInt(segment(1)) <= 4 Then
g.Value.Remove(row)
End If
Next i

Reorder a list of String into a new list

I want to reorder a list of strings into a different list of strings. I am creating multiple loops to do this, however i was wondering if there was a better way to do this.
Dim values As New List(Of String)
For Each val As String In vals
If val.Contains("10") And val.Contains("Year 1") Then
values.Add(val)
End If
Next
For Each val As String In vals
If val.Contains("20") And val.Contains("Year 1") Then
values.Add(val)
End If
Next
There are going to be quite a lot of these loops to do what I want can anyone offer any help or a better way to go about doing this? Note that I want the values list to return 10,10,10,20,20,20 so that lines that have 10 should be added first and lines with 20 then afterwards.
thanks,
Stefan
If you want it to be ordered by the year, this will work:
Dim result = (From item In values
Where item.ToLower Like "*year #*"
Let year = Regex.Match(item.Substring(item.ToLower.IndexOf("year ")), "\d+").Value
Order By year).ToList()
But you've said that you want it to be ordered by 10,20,30 and so on. But i don't see how this should work.
The value could be 8,25,4711 instead and how do want to find it at all in the String?
Contains only checks whether the line contains this substring or not, but you might have this number also in the year part(f.e. year 10) or elsewhere.
So your requirement looks very error-prone and vague.
Edit: If the text is so strict as in your comment's samples, you could simplify it to this, splitting each line into tokens separated by comma:
Dim result = (From item In values
Let Tokens = item.Split({","c}, StringSplitOptions.RemoveEmptyEntries)
Let Count = Int32.Parse(Tokens(3))
Let Year = Int32.Parse(Regex.Match(Tokens(5), "\d+").Value)
Order By Count, Year
Select item).ToList()
Dim values As List(Of String) = vals.OrderBy(Function(s)
If s.Contains("Year 1") Then
If s.Contains("10") Then Return 0
If s.Contains("20") Then Return 1
End If
Return Integer.MaxValue
End Function).ToList()
It sounds like you may also want to create a regular expression for this string, to pull out the fields you care about. That should make this perform a lot better. To help you with the correct expression we'll need to know a lot more about what the strings look like.