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():
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":
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:
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:
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