Results 1 to 12 of 12

Thread: (.NET 3.5) Randomise a List of Items

  1. #1

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    (.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:
    1. Dim numbers As Integer() = {1, 2, 3, 4, 5, 6, 7, 8, 9}
    2.  
    3. For Each number In numbers.Randomise()
    4.     MessageBox.Show(number.ToString())
    5. 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:
    1. Imports System.Runtime.CompilerServices
    2.  
    3. Public Module Enumerable
    4.  
    5.     <Extension>
    6.     Public Function Randomise(Of T)(ByVal items As IEnumerable(Of T)) As IEnumerable(Of T)
    7.         Dim result As T() = Nothing
    8.  
    9.         If items IsNot Nothing Then
    10.             Dim rng As New Random
    11.  
    12.             result = items.ToArray()
    13.  
    14.             'Generate one random value per item.
    15.             Dim keys = Array.ConvertAll(result, Function(item) rng.NextDouble())
    16.  
    17.             'Sort the items by their corresponding random keys, thus randomising them.
    18.             Array.Sort(keys, result)
    19.         End If
    20.  
    21.         Return result
    22.     End Function
    23.  
    24. End Module
    Last edited by jmcilhinney; May 18th, 2017 at 01:02 AM. Reason: Simplified code
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  2. #2
    Fanatic Member
    Join Date
    Oct 2008
    Location
    Dominican Republic
    Posts
    733

    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).
    "In our profession, precision and perfection are not a dispensable luxury, but a simple necessity."
    Niklaus E. Wirth


    Rate any post that helped you, it's a good way of saying thanks
    Please specify your Visual Studio Version!

    Why rating is useful

    My Code Bank Submissions: How to determine Windows Version| Working With Mouse Events | Blocking Input Using API | Get host's IP | Minimize to system tray "animated" | Colored ListBox (custom fonts, colors, highlight) Updated -New Class! | [VS 2008] Strong encryption and hashing class - Updated! 31/August/2009 | Create a shortcut using IWshRuntimeLibrary

  3. #3

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: (.NET 3.5) Randomise a List of Items

    Quote Originally Posted by tassa View Post
    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.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  4. #4
    Fanatic Member
    Join Date
    Oct 2008
    Location
    Dominican Republic
    Posts
    733

    Re: (.NET 3.5) Randomise a List of Items

    Ahahaha, ok ok .
    "In our profession, precision and perfection are not a dispensable luxury, but a simple necessity."
    Niklaus E. Wirth


    Rate any post that helped you, it's a good way of saying thanks
    Please specify your Visual Studio Version!

    Why rating is useful

    My Code Bank Submissions: How to determine Windows Version| Working With Mouse Events | Blocking Input Using API | Get host's IP | Minimize to system tray "animated" | Colored ListBox (custom fonts, colors, highlight) Updated -New Class! | [VS 2008] Strong encryption and hashing class - Updated! 31/August/2009 | Create a shortcut using IWshRuntimeLibrary

  5. #5
    PowerPoster Jenner's Avatar
    Join Date
    Jan 2008
    Location
    Mentor, OH
    Posts
    3,712

    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!
    My CodeBank Submissions: TETRIS using VB.NET2010 and XNA4.0, Strong Encryption Class, Hardware ID Information Class, Generic .NET Data Provider Class, Lambda Function Example, Lat/Long to UTM Conversion Class, Audio Class using BASS.DLL

    Remember to RATE the people who helped you and mark your forum RESOLVED when you're done!

    "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe. "
    - Albert Einstein

  6. #6

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    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.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  7. #7
    PowerPoster Jenner's Avatar
    Join Date
    Jan 2008
    Location
    Mentor, OH
    Posts
    3,712

    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.
    My CodeBank Submissions: TETRIS using VB.NET2010 and XNA4.0, Strong Encryption Class, Hardware ID Information Class, Generic .NET Data Provider Class, Lambda Function Example, Lat/Long to UTM Conversion Class, Audio Class using BASS.DLL

    Remember to RATE the people who helped you and mark your forum RESOLVED when you're done!

    "Two things are infinite: the universe and human stupidity; and I'm not sure about the universe. "
    - Albert Einstein

  8. #8
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    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

  9. #9

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: (.NET 3.5) Randomise a List of Items

    Quote Originally Posted by Evil_Giraffe View Post
    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.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  10. #10
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    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:
    1. Imports System.Runtime.CompilerServices
    2.  
    3. Public Module Enumerable
    4.  
    5.     '//fields
    6.     Private randomInstance As Random
    7.  
    8.     '//methods
    9.     <Extension()> _
    10.     Public Function Randomize(Of T)(ByVal source As IEnumerable(Of T)) As IEnumerable(Of T)
    11.         If source IsNot Nothing Then
    12.             Dim r As Random
    13.             If Enumerable.randomInstance Is Nothing Then
    14.                 Enumerable.randomInstance = New Random()
    15.             End If
    16.             r = Enumerable.randomInstance
    17.             Return source.OrderBy(Function() r.NextDouble()).ToArray()
    18.         End If
    19.         Return Nothing
    20.     End Function
    21.  
    22. End Module

  11. #11
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: (.NET 3.5) Randomise a List of Items

    Good point, but that'd be cleaner with a static initialiser:

    vbnet Code:
    1. '//fields
    2. Private randomInstance As System.Random = New System.Random
    3.  
    4. '//methods
    5. <Extension()> _
    6. Public Function Randomize(Of T)(ByVal source As IEnumerable(Of T)) As IEnumerable(Of T)
    7.     If source IsNot Nothing Then
    8.         Return source.OrderBy(Function() randomInstance.NextDouble()).ToArray()
    9.     End If
    10.     Return Nothing
    11. End Function

  12. #12
    Master Of Orion ForumAccount's Avatar
    Join Date
    Jan 2009
    Location
    Canada
    Posts
    2,802

    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
  •  



Click Here to Expand Forum to Full Width