|
-
Sep 28th, 2009, 10:32 PM
#1
(.NET 3.5) Randomise a List of Items
C# version here.
The following is an extension method for getting the contents of any IEnumerable(Of T) object, e.g. an array or List(Of T), in random order:
Code:
Imports System.Runtime.CompilerServices
Public Module Enumerable
<Extension()> _
Public Function Randomise(Of T)(ByVal items As IEnumerable(Of T)) As IEnumerable(Of T)
Dim result As T() = Nothing
If items IsNot Nothing Then
Dim rng As New Random
result = (From item In items Order By rng.NextDouble()).ToArray()
End If
Return result
End Function
End Module
Note that it obeys the rules for extension methods, i.e. it's a member of a module, is decorated with the Extension attribute and takes the target type as the first argument. You might use it something like this:
vb.net Code:
Dim numbers As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9} For Each number In numbers.Randomise() MessageBox.Show(number.ToString()) Next
Try running that repeatedly and you'll get a different order each time.
Note that you could write a regular method that does the same thing in earlier versions.
EDIT: It's been brought to my attention that there is a significant issue with this code that, while unlikely to be encountered, could cause the operation to fail. Here's a reimplementation that isn't susceptible to this issue:
vb.net Code:
Imports System.Runtime.CompilerServices Public Module Enumerable <Extension> Public Function Randomise(Of T)(ByVal items As IEnumerable(Of T)) As IEnumerable(Of T) Dim result As T() = Nothing If items IsNot Nothing Then Dim rng As New Random result = items.ToArray() 'Generate one random value per item. Dim keys = Array.ConvertAll(result, Function(item) rng.NextDouble()) 'Sort the items by their corresponding random keys, thus randomising them. Array.Sort(keys, result) End If Return result End Function End Module
Last edited by jmcilhinney; May 18th, 2017 at 01:02 AM.
Reason: Simplified code
-
Sep 29th, 2009, 07:30 AM
#2
Fanatic Member
Re: (.NET 3.5) Randomise a List of Items
Great code John! (Btw, I think it's randomiZe, I have a thing for spelling, if not, blame Google :P).
-
Sep 29th, 2009, 07:50 AM
#3
Re: (.NET 3.5) Randomise a List of Items
 Originally Posted by tassa
Great code John! (Btw, I think it's randomiZe, I have a thing for spelling, if not, blame Google :P).
It's "randomize" if you speak American, "randomise" if you speak English.
-
Sep 29th, 2009, 09:38 AM
#4
Fanatic Member
Re: (.NET 3.5) Randomise a List of Items
Ahahaha, ok ok .
-
Sep 29th, 2009, 03:58 PM
#5
Re: (.NET 3.5) Randomise a List of Items
Wow, it's got LINQ, Generic types and an extension method; that's some pretty powerful coding there! Kudos!
-
Sep 29th, 2009, 08:38 PM
#6
Re: (.NET 3.5) Randomise a List of Items
Note that I have simplified the code a little from the original. The new code will not throw an exception if you call it on a null reference but it will return a null reference. As such, an exception will be thrown if you try to enumerate the result, which did not happen with the old code. You could easily adjust the code to return an empty array if a null reference is passed in to avoid that if you think it's appropriate.
-
Sep 30th, 2009, 07:34 AM
#7
Re: (.NET 3.5) Randomise a List of Items
I already made that update when I copied it into my extensibility utility module last night. I like my methods to return Nothing in most null reference errors and thus, can check for that condition prior to enumeration.
-
Dec 6th, 2010, 07:14 PM
#8
Re: (.NET 3.5) Randomise a List of Items
One small issue with the above code. You're creating a new instance of the Random class in the extension method itself. Thus if you call it multiple times in short succession, each execution will get a Random instance initialised with the same seed. Instead, it is better to create a single Random instance for your entire app and pass that reference around, like this:
Code:
<Extension()> _
Public Function Randomise(Of T)(ByVal items As IEnumerable(Of T), ByRef random As System.Random) As IEnumerable(Of T)
Dim rng = random
Dim result As T() = Nothing
If items IsNot Nothing Then
result = (From item In items Order By rng.NextDouble()).ToArray()
End If
Return result
End Function
The following calling code demonstrates the issue with the original method:
Code:
Sub Main()
Dim numbers As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9}
For i = 1 To 2
Console.Write("Run {0}: ", i)
For Each number In numbers.Randomise()
Console.Write("{0} ", number)
Next
Console.WriteLine()
Next
Console.WriteLine()
Dim rng As New System.Random
For i = 1 To 2
Console.Write("Run {0}: ", i)
For Each number In numbers.Randomise(rng)
Console.Write("{0} ", number)
Next
Console.WriteLine()
Next
Console.ReadLine()
End Sub
Gives the following output:
Code:
Run 1: 2 5 3 6 4 1 8 7 9
Run 2: 2 5 3 6 4 1 8 7 9
Run 1: 2 5 3 6 4 1 8 7 9
Run 2: 5 6 7 9 2 8 1 3 4
Last edited by Evil_Giraffe; Dec 6th, 2010 at 07:21 PM.
Reason: Tidier code output
-
Dec 6th, 2010, 07:34 PM
#9
Re: (.NET 3.5) Randomise a List of Items
 Originally Posted by Evil_Giraffe
One small issue with the above code. You're creating a new instance of the Random class in the extension method itself. Thus if you call it multiple times in short succession, each execution will get a Random instance initialised with the same seed.
I'm well aware of that potential issue with the Random class. In this case though, what are the chances that you will have multiple lists that you want to randomise in such quick succession that each Random object will use the same seed?
The idea of using a single Random object for an entire application and passing it around is not really practical in many cases. This extension method would be basically pointless if you had to create your own Random object to use it. In that case you may as well just use your own LINQ query.
-
Dec 7th, 2010, 06:02 PM
#10
Re: (.NET 3.5) Randomise a List of Items
You could just put the Random object as a Private field inside the Module:
vb.net Code:
Imports System.Runtime.CompilerServices
Public Module Enumerable
'//fields
Private randomInstance As Random
'//methods
<Extension()> _
Public Function Randomize(Of T)(ByVal source As IEnumerable(Of T)) As IEnumerable(Of T)
If source IsNot Nothing Then
Dim r As Random
If Enumerable.randomInstance Is Nothing Then
Enumerable.randomInstance = New Random()
End If
r = Enumerable.randomInstance
Return source.OrderBy(Function() r.NextDouble()).ToArray()
End If
Return Nothing
End Function
End Module
-
Dec 7th, 2010, 07:43 PM
#11
Re: (.NET 3.5) Randomise a List of Items
Good point, but that'd be cleaner with a static initialiser:
vbnet Code:
'//fields Private randomInstance As System.Random = New System.Random '//methods <Extension()> _ Public Function Randomize(Of T)(ByVal source As IEnumerable(Of T)) As IEnumerable(Of T) If source IsNot Nothing Then Return source.OrderBy(Function() randomInstance.NextDouble()).ToArray() End If Return Nothing End Function
-
Dec 8th, 2010, 10:03 AM
#12
Re: (.NET 3.5) Randomise a List of Items
Except if you don't use the function you just created a object that doesn't ever get used.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|