How to randomly select strings vb.net - vb.net

Is there a simple solution to select random strings in vb.net? I have a list of about twenty paragraphs where three need to go after each other and I want it to be random. Do I have to create a variable? Or is there a command that can be run on a button click?

One (fairly easy way) to accomplish this would be to have a collection of the paragraphs you want to use, and then use PeanutButter.RandomValueGen from the Nuget package PeanutButter.RandomGenerators (it's open-source too)
RandomValueGen.GetRandomFrom takes a collection of anything and returns a random item from the collection. As a bonus, you can specify a params list of values not to pick, so you can ensure that your paragraphs aren't repeated.
Whilst the library is written in C#, it can obviously be used from any .NET project. There are a lot of other generator methods on RandomValueGen too, if you're interested.
Full disclosure: I'm the author.

If you have a normal list, this should work:
If not, write what kind of list you have.
Dim rn As New Random
Dim selct As String = lst(rn.Next(0, lst.Count - 1))
selct is the output.
Replace lst with your list name.

if you don't want to have a dependency or need to stay on 4.0 for some odd reason or reason X, you can always try this instead
Private rnd As New Random
Public Function GetRandom(input As IEnumerable(Of String), itemToGet As Integer) As List(Of String)
If input.Count < itemToGet Then
Throw New Exception("List is too small")
End If
Dim copy = input.ToList
Dim result As New List(Of String)
Dim item As Integer
While itemToGet > 0
item = rnd.Next(0, copy.Count)
result.Add(copy(item))
copy.RemoveAt(item)
itemToGet -= 1
End While
Return result
End Function

Related

Streamwriter: write two listboxes on the same row

I am trying to write, in order to export on txt file, information in two listbox with the same number of rows. I have to export them with the following format: Listbox1, Listbox2. In order to do this, I've tried to use the following code:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As Object In Form3.ListBox1.Items And Form3.ListBox2.Items
writer.WriteLine(o)
Next
End Using
I'm receiving the following error:
BC30452 Operator 'And' is not defined for types 'ListBox.ObjectCollection' and 'ListBox.ObjectCollection'.
I've also tried to perform three For Each loops, the first for the LB1, the second for the commas and the third for LB2, but I'm having it exported with content on single lines. How could I solve this?
If you use Enumerable.Zip, as suggested in another answer, then you can make the code more succinct by doing away with the explicit loop:
File.WriteAllLines(SaveFileDialog1.FileName,
Form3.ListBox1.
Items.
Cast(Of Object).
Zip(Form3.ListBox2.
Items.
Cast(Of Object),
Function(x1, x2) $"{x1}, {x2}"))
If you didn't use Zip then you can use a loop this way:
Dim items1 = Form3.ListBox1.Items
Dim items2 = Form3.ListBox2.Items
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For i = 0 To Math.Min(items1.Count, items2.Count)
writer.WriteLine($"{items1(i)}, {items2(i)}")
Next
End Using
The Math.Min part is just in case there are different numbers of items in each ListBox. If you know there aren't then you can do away with that and just use one Count. If there might be different counts but you want to output all items then the code would become slightly more complex to handle that.
As the error message says, the syntax you attempted is simply not valid. There's no feature in VB.NET that does that sort of thing.
However, the .NET Framework API does provide a means for something similar, which would probably work in your case. See Enumerable.Zip(). You can use it like this:
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As String In Form3.ListBox1.Items.Cast(Of Object).Zip(Form3.ListBox2.Items.Cast(Of Object), Function(x1, x2) x1 & ", " & x2)
writer.WriteLine(o)
Next
End Using
Since you said that both list boxes have the same number of items we can use the number of items in the first listbox less one (indexes start at zero) in a For loop.
I used a StringBuilder so the code does not have to throw away and create a new string on each iteration.
I used an interpolated string indicate by the $ preceding the string. This means I can insert variables in braces, right along with literals.
Call .ToString on the StringBuilder to write to the text file.
Private Sub SaveListBoxes()
Dim sb As New StringBuilder
For i = 0 To ListBox1.Items.Count - 1
sb.AppendLine($"{ListBox1.Items(i)}, {ListBox2.Items(i)}")
Next
File.WriteAllText("C:\Users\xxx\Desktop\ListBoxText.txt", sb.ToString)
End Sub

Vb Net check if arrayList contains a substring

I am using myArrayList.Contains(myString) and myArrayList.IndexOf(myString) to check if arrayList contains provided string and get its index respectively.
But, How could I check if contains a substring?
Dim myArrayList as New ArrayList()
myArrayList.add("sub1;sub2")
myArrayList.add("sub3;sub4")
so, something like, myArrayList.Contains("sub3") should return True
Well you could use the ArrayList to search for substrings with
Dim result = myArrayList.ToArray().Any(Function(x) x.ToString().Contains("sub3"))
Of course the advice to use a strongly typed List(Of String) is absolutely correct.
As far as your question goes, without discussing why do you need ArrayList, because array list is there only for backwards compatibility - to select indexes of items that contain specific string, the best performance you will get here
Dim indexes As New List(Of Integer)(100)
For i As Integer = 0 to myArrayList.Count - 1
If DirectCast(myArrayList(i), String).Contains("sub3") Then
indexes.Add(i)
End If
Next
Again, this is if you need to get your indexes. In your case, ArrayList.Contains - you testing whole object [string in your case]. While you need to get the string and test it's part using String.Contains
If you want to test in non case-sensitive manner, you can use String.IndexOf

Best way to iterate through Hashtable and conditionally remove entries in VB.NET

In VB.NET, I have a HashTable that I would like to iterate through and conditionally remove entries from. I've written the following code that does the job perfectly, but I'd like to know if there are any creative ways to simplify the code. It just doesn't seem right to have to create a second list to perform this operation.
Here's what I've written:
Dim ModsToRemove As New List(Of String)
For Each ModKey As DictionaryEntry In ModHashTable
If ModKey.Key.ToString.Contains("Criteria") Then
ModsToRemove.Add(ModKey.Key.ToString)
End If
Next
For Each ModKey As String In ModsToRemove
ModHashTable.Remove(ModKey)
Next
Is there another way to perform the same operation that doesn't require the creation of a second list and a second loop? Is it possible to remove entries from something you are iterating through without throwing an error in VB.NET? Is doing so universally a bad idea?
With a little bit of help from Resharper and LINQ, you can simplify your expression in the following ways.
This code block here can be rewritten to use LINQ instead of the embedded IF statement
For Each ModKey As DictionaryEntry In ModHashTable
If ModKey.Key.ToString.Contains("Criteria") Then
ModsToRemove.Add(ModKey.Key.ToString)
End If
Next
Is equivalent to
Dim modsToRemove As List(Of String) = (From modKey As DictionaryEntry In
modHashTable Where modKey.Key.ToString.Contains("Criteria")
Select modKey.Key.ToString).ToList()
Combining this with your actual loop to remove the items from the Hashtable, you should be able to get the equivalent functionality of your example above with the following 3 lines of code:
For Each key As String In (From modkey As DictionaryEntry In modHashTable Where modkey.Key.ToString.Contains("Criteria") Select modkey.Key.ToString).ToList()
modHashTable.Remove(key)
Next

VB - How do you remove "empty" items from a generic list?

I have a VB.NET (2010) project that contains a generic list, and I'm trying to figure out how to remove any "empty" items from the list. When I say "empty", I mean any item that does not contain any actual characters (but it may contain any amount of whitespace, or no whitespace at all).
For example, let's say this is my list...
Dim MyList As New List(Of String)
MyList.Add("a")
MyList.Add("")
MyList.Add("b")
MyList.Add(" ")
MyList.Add("c")
MyList.Add(" ")
MyList.Add("d")
I need it so that if I did a count on that list, it would return 4 items, instead of 7. For example...
Dim ListCount As Integer = MyList.Count
MessageBox.Show(ListCount) ' Should show "4"
It would be nice if there was something like...
MyList.RemoveEmpty
Anyways... I've been searching Google for a solution to this for the past few hours, but haven't been able to turn up anything so far. So... any ideas?
BTW, I'm targeting the .NET 2.0 framework for this project.
Thanks in advance!
You can use List.RemoveAll
MyList.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
If you don't use at least .NET 4, you can't use String.IsNullOrWhiteSpace. Then you can implement the method yourself:
Public Shared Function IsNullOrWhiteSpace(value As String) As Boolean
If value Is Nothing Then
Return True
End If
For i As Integer = 0 To value.Length - 1
If Not Char.IsWhiteSpace(value(i)) Then
Return False
End If
Next
Return True
End Function
Note that Char.IsWhiteSpace is there since 1.1.
The post marked as solution didn't works for me.
Try this:
MyList.AsEnumerable().Where(Function (x) Not String.IsNullOrEmpty(x)).ToList
This must returns you a list without empty values.

Finding distinct lines in large datatables

Currently we have a large DataTable (~152k rows) and are doing a for each over this to find a sub set of distinct entries (~124K rows). This is currently taking about 14 minutes to run which is just far too long.
As we are stuck in .NET 2.0 as our reporting won't work with VS 2008+ I can't use linq, though I don't know if this will be any faster in fairness.
Is there a better way to find the distinct lines (invoice numbers in this case) other than this for each loop?
This is the code:
Public Shared Function SelectDistinctList(ByVal SourceTable As DataTable, _
ByVal FieldName As String) As List(Of String)
Dim list As New List(Of String)
For Each row As DataRow In SourceTable.Rows
Dim value As String = CStr(row(FieldName))
If Not list.Contains(value) Then
list.Add(value)
End If
Next
Return list
End Function
Using a Dictionary rather than a List will be quicker:
Dim seen As New Dictionary(Of String, String)
...
If Not seen.ContainsKey(value) Then
seen.Add(value, "")
End If
When you search a List, you're comparing each entry with value, so by the end of the process you're doing ~124K comparisons for each record. A Dictionary, on the other hand, uses hashing to make the lookups much quicker.
When you want to return the list of unique values, use seen.Keys.
(Note that you'd ideally use a Set type for this, but .NET 2.0 doesn't have one.)