1.

Solve : [VB .Net] Pick a random entry in a List(string)?

Answer»

Pretty much I have:

Code: [Select]Dim LI as List(string)
li.add("I'm good")
li.add("I'm better")
li. add("I'm best")
How do i randomly select one of these entries? Code: [Select]li.Item(Int(Rnd() * (li.Count - 1)))

will pick a random item.
I ever told you, BC_Programmer, I love you

Another quick question --

If i have the code: Code: [Select]Dim di As New IO.DirectoryInfo("C:\music")
How do i get di to include all sub-directories? Code: [Select]Sub Main()


        Dim dirs As IO.DirectoryInfo()
        Dim loopdir As IO.DirectoryInfo
        dirs = New IO.DirectoryInfo("C:\music").GetDirectories
        For Each loopdir In dirs
            Console.WriteLine(loopdir.FullName)


        Next
        Console.ReadKey()
    End Sub



It's a bit more involved; in this case I didn't bother STORING a directoryInfo for the "C:\Music" directory, rather opting to directly acquire it's subdirs. The "GetDirectories" Method of the DirecoryInfo Class returns an array of DirectoryInfo classes that you can enumerate using a For...Each Loop; in my example above it SIMPLY prints off each directory name, but you can do whatever you wish with them.I realise this is an old thread but I was wondering what  ('* li.Count - 1)))' did in the code above..... Quote from: finchy109 on April 10, 2013, 08:52:04 AM

I realise this is an old thread but I was wondering what  ('* li.Count - 1)))' did in the code above.....
the value used is li.Count-1 because the Items Accessor is zero-based. However, even so there is actually a bug in the original solution that prevents the last element of a list from ever being chosen.

It's actually a bug, somewhat. I used -1 because the Item accessor method takes a zero-based index and li.Count retrieves the total NUMBER of elements, so a 4-item list would return 4 for Count, but the fourth item would be accessed with li.Items(3).

However, the "bug" is because rnd returns a number between 0 and 1 exclusive- that is, the resulting value will never be 1. The Int function truncates the value, so the resulting index will never actually be the last index of the list.

The bugfix would be to use Math.Round instead of Int():

Code: [Select]li.Item(Math.Round(Rnd() * (li.Count - 1)))


Full disclosure, I didn't actually use the original code, so it wasn't fully tested. My preferred method of choosing an Item from an Array or other collection is a Generic Method I wrote in C#.

To make ammends for my 4 year old transgression, I will post that here, after porting it to VB.NET. The original C# implementation was called "Choose" and so was my VB.NET version until I noticed the existence of the legacy Choose() Function from VB6, when I renamed it to "Pick":

Code: [Select]    Public Function Pick(Of T)(ChooseArray As IEnumerable(Of T)) As T
        If rgen Is Nothing Then rgen = New Random()

        Dim sorttest As SortedList(Of Double, T) = New SortedList(Of Double, T)()
        For Each loopvalue As T In ChooseArray
            Dim rgg As Double = rgen.NextDouble()
            Do While sorttest.ContainsKey(rgg)
                rgg = rgen.NextDouble()
            Loop
            sorttest.Add(rgg, loopvalue)

        Next

        Return sorttest.First().Value
    End Function

As a generic method, it works with an Array of Any type, and Any collection, because it accepts any IEnumerable implementation.

If one is targeting VB11, you can even use the ported version of the method that acts as an Iterator Method:

Code: [Select]    Public Iterator Function Pick(Of T)(ChooseArray As IEnumerable(Of T), NumItems As Integer) As IEnumerable(Of T)
        If rgen Is Nothing Then rgen = New Random()

        Dim sorttest As SortedList(Of Double, T) = New SortedList(Of Double, T)()
        Dim ResultTest As List(Of T) = New List(Of T)

        For Each loopvalue As T In ChooseArray
            Dim rgg As Double = rgen.NextDouble()
            Do While sorttest.ContainsKey(rgg)
                rgg = rgen.NextDouble()
            Loop
            sorttest.Add(rgg, loopvalue)

        Next

        Dim I As Integer

        Dim DictEnumerator As IEnumerator(Of KeyValuePair(Of Double, T)) = sorttest.GetEnumerator()

        Do While DictEnumerator.MoveNext() And NumItems > (I)
            Yield DictEnumerator.Current.Value
            I = I + 1
        Loop

    End Function

The test Code I used that shows these methods being used:
Code: [Select]  Sub Main()

        Dim SelectFrom As Integer() = Enumerable.Range(0, 100).ToArray()
        For I As Integer = 0 To 100
            Console.WriteLine(Pick(SelectFrom))
        Next

        For I As Integer = 0 To 100

            For Each iterate In Pick(SelectFrom, 4)
                Console.Write(Str(iterate) + ",")
            Next
            Console.WriteLine()

        Next

        Console.ReadKey()
    End Sub



Discussion

No Comment Found