Results 1 to 7 of 7

Thread: Useful Extension Methods

  1. #1

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

    Useful Extension Methods

    C# version here.

    This is a library of extension methods I've been building to make various things easier and or cleaner in code. The solution was created in VS 2017 and all initial projects target .NET 4.7.1. The solution contains VB and C# projects and I will provide some extra explanation and examples of the VB code here. The TestBed projects are WinForms and are intended to provide tests for pretty much all the included extensions. They are currently incomplete but I will replace the attachment when I have finished them.

    You can build the solution and use the compiled libraries with your own applications, or I'm happy for anyone to copy whichever parts of the code they find useful and use them directly in their own projects. While I don't require explicit credit, I also require that you don't try to take explicit credit for the code.

    EDIT 23 April, 2018: Improved TestBed projects. Removed ParamArray from 'weightings' parameters of existing RandomExtensions.NextWithWeighting overloads. Added new overloads of RandomExtensions.NextWithWeighting enabling implicitly specifying weightings for some possible outcomes.

    EDIT 23 April, 2018: Improved TestBed projects. Fixed bug in Random project. Fixed bug in VB Numerics project.

    EDIT 3 May, 2018: Changed Image project to Drawing and added DrawCircle and DrawTriangle extensions for Graphics class. Add ToLines method to String extensions.

    EDIT 19 May, 2018: Added ArrayExtensions to Enumerable project. Added unit test projects.
    Attached Files Attached Files

  2. #2

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

    Useful Extension Methods for Images

    vb.net Code:
    1. ' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s.
    2. Public Function ToBytes(source As Image) As Byte()
    3. ' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s in the specified format.
    4. Public Function ToBytes(source As Image, format As ImageFormat) As Byte()
    5. ' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s, with the specified encoder and image-encoder parameters.
    6. Public Function ToBytes(source As Image, encoder As ImageCodecInfo, encoderParams As EncoderParameters) As Byte()
    vb.net Code:
    1. ' Converts an array of <see cref="Byte"/> containing image data into an <see cref="Image"/> object.
    2. Public Function ToImage(source As IEnumerable(Of Byte)) As Image
    The methods in the Image project are basically about converting between an Image object and a Byte array and vice versa. These operations are required when saving and loading images in databases, as well as in other situations.
    vb.net Code:
    1. Imports System.Drawing
    2. Imports System.Drawing.Imaging
    3. Imports System.IO
    4. Imports System.Runtime.CompilerServices
    5.  
    6. ''' <summary>
    7. ''' Contains methods that extend the <see cref="Image"/> class.
    8. ''' </summary>
    9. Public Module ImageExtensions
    10.  
    11.     ''' <summary>
    12.     ''' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s.
    13.     ''' </summary>
    14.     ''' <param name="source">
    15.     ''' The inoput image.
    16.     ''' </param>
    17.     ''' <returns>
    18.     ''' An array containing the image data.
    19.     ''' </returns>
    20.     <Extension>
    21.     Public Function ToBytes(source As Image) As Byte()
    22.         Return source.ToBytes(source.RawFormat)
    23.     End Function
    24.  
    25.     ''' <summary>
    26.     ''' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s in the specified format.
    27.     ''' </summary>
    28.     ''' <param name="source">
    29.     ''' The inoput image.
    30.     ''' </param>
    31.     ''' <param name="format">
    32.     ''' The <see cref="ImageFormat"/> for this <see cref="Image"/>.
    33.     ''' </param>
    34.     ''' <returns>
    35.     ''' An array containing the image data.
    36.     ''' </returns>
    37.     <Extension>
    38.     Public Function ToBytes(source As Image, format As ImageFormat) As Byte()
    39.         Using stream As New MemoryStream
    40.             source.Save(stream, format)
    41.  
    42.             Return stream.ToArray()
    43.         End Using
    44.     End Function
    45.  
    46.     ''' <summary>
    47.     ''' Saves an <see cref="Image"/> to an array of <see cref="Byte"/>'s, with the specified encoder and image-encoder parameters.
    48.     ''' </summary>
    49.     ''' <param name="source">
    50.     ''' The inoput image.
    51.     ''' </param>
    52.     ''' <param name="encoder">
    53.     ''' The <see cref="ImageCodecInfo"/> for this <see cref="Image"/>.
    54.     ''' </param>
    55.     ''' <param name="encoderParams">
    56.     ''' An <see cref="EncoderParameters"/> to use for this <see cref="Image"/>.
    57.     ''' </param>
    58.     ''' <returns>
    59.     ''' An array containing the image data.
    60.     ''' </returns>
    61.     <Extension>
    62.     Public Function ToBytes(source As Image, encoder As ImageCodecInfo, encoderParams As EncoderParameters) As Byte()
    63.         Using stream As New MemoryStream
    64.             source.Save(stream, encoder, encoderParams)
    65.  
    66.             Return stream.ToArray()
    67.         End Using
    68.     End Function
    69.  
    70. End Module
    vb.net Code:
    1. Imports System.Drawing
    2. Imports System.IO
    3. Imports System.Runtime.CompilerServices
    4.  
    5. ''' <summary>
    6. ''' Contains methods that extend the <see cref="IEnumerable(Of T)"/> interface.
    7. ''' </summary>
    8. Public Module EnumerableExtensions
    9.  
    10.     ''' <summary>
    11.     ''' Converts an array of <see cref="Byte"/> containing image data into an <see cref="Image"/> object.
    12.     ''' </summary>
    13.     ''' <param name="source">
    14.     ''' The input byte array.
    15.     ''' </param>
    16.     ''' <returns>
    17.     ''' The image represented by the input data.
    18.     ''' </returns>
    19.     <Extension>
    20.     Public Function ToImage(source As IEnumerable(Of Byte)) As Image
    21.         Using stream As New MemoryStream(source.ToArray())
    22.             Return Image.FromStream(stream)
    23.         End Using
    24.     End Function
    25.  
    26. End Module
    These methods simply encapsulate the use of a MemoryStream go between an Image and a Byte array or vice versa. Saving an Image to a database might look like this:
    Code:
    Using connection As New SqlConnection("connection string here"),
          command As New SqlCommand("UPDATE MyTable SET Picture = @Picture WHERE Id = @Id", connection)
        command.Parameters.Add("@Picture", SqlDbType.VarBinary, -1).Value = PictureBox1.Image.ToBytes()
        command.Parameters.Add("@Id", SqlDbType.Int).Value = 1
    
        connection.Open()
        command.ExecuteNonQuery()
    End Using
    while retrieving that Image might look like this:
    Code:
    Using connection As New SqlConnection("connection string here"),
          command As New SqlCommand("SELECT Picture FROM MyTable WHERE Id = @Id", connection)
        command.Parameters.Add("@Id", SqlDbType.Int).Value = 1
    
        connection.Open()
    
        Dim data = DirectCast(command.ExecuteScalar(), Byte())
    
        PictureBox1.Image = data.ToImage()
    End Using
    The three overloads of ToBytes correlate to the three overloads of Image.Save that have a first parameter of type String, where one saves using the raw format of the Image object, one takes a format to save in and the third takes an encoder and encoding parameters.

  3. #3

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

    Useful Extension Methods for Random Numbers

    vb.net Code:
    1. ' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    2. Public Function NextWithWeighting(source As Random,
    3.                                   maxValue As Integer,
    4.                                   weightings As Integer()) As Integer
    5. ' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    6. Public Function NextWithWeighting(source As Random,
    7.                                   minValue As Integer,
    8.                                   maxValue As Integer,
    9.                                   weightings As Integer()) As Integer
    10. ' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    11. Public Function NextWithWeighting(source As Random,
    12.                                   maxValue As Integer,
    13.                                   weightings As IDictionary(Of Integer, Integer)) As Integer
    14. ' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    15. Public Function NextWithWeighting(source As Random,
    16.                                   minValue As Integer,
    17.                                   maxValue As Integer,
    18.                                   weightings As IDictionary(Of Integer, Integer)) As Integer
    19. ' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    20. Public Function NextWithWeighting(source As Random,
    21.                                   maxValue As Integer,
    22.                                   weightings As IDictionary(Of Integer, Integer),
    23.                                   defaultWeighting As Integer) As Integer
    24.         Return source.NextWithWeighting(0, maxValue, weightings, defaultWeighting)
    25. ' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    26. Public Function NextWithWeighting(source As Random,
    27.                                   minValue As Integer,
    28.                                   maxValue As Integer,
    29.                                   weightings As IDictionary(Of Integer, Integer),
    30.                                   defaultWeighting As Integer) As Integer
    The methods in the Random project enable you to generate a random number in a range where each number can have a different likelihood of being generated.
    vb.net Code:
    1. Imports System.Runtime.CompilerServices
    2.  
    3. ''' <summary>
    4. ''' Contains methods that extend the <see cref="Random"/> class.
    5. ''' </summary>
    6. Public Module RandomExtensions
    7.  
    8.     Private Const DEFAULT_WEIGHTING As Integer = 1
    9.  
    10.     ''' <summary>
    11.     ''' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    12.     ''' </summary>
    13.     ''' <param name="source">
    14.     ''' The <see cref="Random"/> object to use to generate the number.
    15.     ''' </param>
    16.     ''' <param name="maxValue">
    17.     ''' The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to 0.
    18.     ''' </param>
    19.     ''' <param name="weightings">
    20.     ''' The weightings for each of the possible outcomes.
    21.     ''' </param>
    22.     ''' <returns>
    23.     ''' A 32-bit signed integer that is greater than or equal to 0, and less than maxValue; that is, the range of return values ordinarily includes 0 but not maxValue. However, if maxValue equals 0, maxValue is returned.
    24.     ''' </returns>
    25.     ''' <remarks>
    26.     ''' A non-negative weighting must be provided for each possible outcome.  Weightings are a proportion of the total of all weightings.  They are not percentages.
    27.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    28.     ''' </remarks>
    29.     <Extension>
    30.     Public Function NextWithWeighting(source As Random,
    31.                                       maxValue As Integer,
    32.                                       weightings As Integer()) As Integer
    33.         Return source.NextWithWeighting(0, maxValue, weightings)
    34.     End Function
    35.  
    36.     ''' <summary>
    37.     ''' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    38.     ''' </summary>
    39.     ''' <param name="source">
    40.     ''' The <see cref="Random"/> object to use to generate the number.
    41.     ''' </param>
    42.     ''' <param name="minValue">
    43.     ''' The inclusive lower bound of the random number returned.
    44.     ''' </param>
    45.     ''' <param name="maxValue">
    46.     ''' The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.
    47.     ''' </param>
    48.     ''' <param name="weightings">
    49.     ''' The weightings for each of the possible outcomes.
    50.     ''' </param>
    51.     ''' <returns>
    52.     ''' A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
    53.     ''' </returns>
    54.     ''' <remarks>
    55.     ''' A non-negative weighting must be provided for each possible outcome.  Weightings are a proportion of the total of all weightings.  They are not percentages.
    56.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    57.     ''' </remarks>
    58.     <Extension>
    59.     Public Function NextWithWeighting(source As Random,
    60.                                       minValue As Integer,
    61.                                       maxValue As Integer,
    62.                                       weightings As Integer()) As Integer
    63.         If minValue > maxValue Then
    64.             Throw New ArgumentOutOfRangeException($"'{NameOf(minValue)}' cannot be greater than {NameOf(maxValue)}.", NameOf(minValue))
    65.         End If
    66.  
    67.         If maxValue > minValue AndAlso weightings.Length <> maxValue - minValue Then
    68.             Throw New ArgumentException("A weighting must be provided for all possible outcomes.", NameOf(weightings))
    69.         End If
    70.  
    71.         If weightings.Any(Function(n) n < 0) Then
    72.             Throw New ArgumentException("All weightings must be greater than zero.", NameOf(weightings))
    73.         End If
    74.  
    75.         Dim totalWeightings As Integer
    76.  
    77.         Try
    78.             totalWeightings = weightings.Sum()
    79.         Catch ex As OverflowException
    80.             Throw New ArgumentOutOfRangeException("The sum of all weightings must not be greater than Int32.MaxValue.", ex)
    81.         End Try
    82.  
    83.         If totalWeightings = 0 Then
    84.             Throw New ArgumentException("The sum of all weightings must be greater than zero.", NameOf(weightings))
    85.         End If
    86.  
    87.         If minValue = maxValue OrElse minValue = maxValue + 1 Then
    88.             'There is only one possible value.
    89.             Return minValue
    90.         End If
    91.  
    92.         'Generate a number in the range 0 to 1 less than the total weightings.
    93.         Dim number = source.Next(totalWeightings)
    94.  
    95.         Dim runningWeighting As Integer
    96.  
    97.         'For each weighting, check whether the number generated falls in that interval.
    98.         For i = 0 To weightings.GetUpperBound(0)
    99.             'Sum the weightings so far.
    100.             'E.g. if the weightings are 10, 20, 30 and 40 then the running weighting for each iteration will be:
    101.             'i = 0: runningWeighting = 0 + 10 = 10
    102.             'i = 1: runningWeighting = 10 + 20 = 30
    103.             'i = 2: runningWeighting = 30 + 30 = 60
    104.             'i = 3: runningWeighting = 60 + 40 = 100
    105.             runningWeighting += weightings(i)
    106.  
    107.             'There is no interval until the running weighting is greater than zero.
    108.             If runningWeighting > 0 AndAlso number < runningWeighting Then
    109.                 'The number generated falls within the current weighting interval so get the value from the original range that corresponds to that interval.
    110.                 Return minValue + i
    111.             End If
    112.         Next
    113.  
    114.         'If we end up here then something was wrong with the interval and/or the weightings.
    115.         'The validation at the top of the method should ensure that such cases are always caught first.
    116.         Throw New Exception("An unexpected error occurred.")
    117.     End Function
    118.  
    119.     ''' <summary>
    120.     ''' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    121.     ''' </summary>
    122.     ''' <param name="source">
    123.     ''' The <see cref="Random"/> object to use to generate the number.
    124.     ''' </param>
    125.     ''' <param name="maxValue">
    126.     ''' The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to 0.
    127.     ''' </param>
    128.     ''' <param name="weightings">
    129.     ''' A dictionary containing the weightings for some Or all of the possible outcomes, where the keys are the possible outcomes And the values are the weightings.
    130.     ''' </param>
    131.     ''' <returns>
    132.     ''' A 32-bit signed integer that is greater than or equal to 0, and less than maxValue; that is, the range of return values ordinarily includes 0 but not maxValue. However, if maxValue equals 0, maxValue is returned.
    133.     ''' </returns>
    134.     ''' <remarks>
    135.     ''' A weighting of 1 is used for any values for which a weighting is not explicitly provided.  Weightings are a proportion of the total of all weightings.  They are not percentages.
    136.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    137.     ''' </remarks>
    138.     <Extension>
    139.     Public Function NextWithWeighting(source As Random,
    140.                                       maxValue As Integer,
    141.                                       weightings As IDictionary(Of Integer, Integer)) As Integer
    142.         Return source.NextWithWeighting(0, maxValue, weightings)
    143.     End Function
    144.  
    145.     ''' <summary>
    146.     ''' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    147.     ''' </summary>
    148.     ''' <param name="source">
    149.     ''' The <see cref="Random"/> object to use to generate the number.
    150.     ''' </param>
    151.     ''' <param name="minValue">
    152.     ''' The inclusive lower bound of the random number returned.
    153.     ''' </param>
    154.     ''' <param name="maxValue">
    155.     ''' The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.
    156.     ''' </param>
    157.     ''' <param name="weightings">
    158.     ''' A dictionary containing the weightings for some Or all of the possible outcomes, where the keys are the possible outcomes And the values are the weightings.
    159.     ''' </param>
    160.     ''' <returns>
    161.     ''' A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
    162.     ''' </returns>
    163.     ''' <remarks>
    164.     ''' A weighting of 1 is used for any values for which a weighting is not explicitly provided.  Weightings are a proportion of the total of all weightings.  They are not percentages.
    165.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    166.     ''' </remarks>
    167.     <Extension>
    168.     Public Function NextWithWeighting(source As Random,
    169.                                       minValue As Integer,
    170.                                       maxValue As Integer,
    171.                                       weightings As IDictionary(Of Integer, Integer)) As Integer
    172.         Return source.NextWithWeighting(minValue, maxValue, weightings, DEFAULT_WEIGHTING)
    173.     End Function
    174.  
    175.     ''' <summary>
    176.     ''' Returns a non-negative random integer that is less than the specified maximum where each value in that range has a weighted probability.
    177.     ''' </summary>
    178.     ''' <param name="source">
    179.     ''' The <see cref="Random"/> object to use to generate the number.
    180.     ''' </param>
    181.     ''' <param name="maxValue">
    182.     ''' The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to 0.
    183.     ''' </param>
    184.     ''' <param name="weightings">
    185.     ''' A dictionary containing the weightings for some Or all of the possible outcomes, where the keys are the possible outcomes And the values are the weightings.
    186.     ''' </param>
    187.     ''' <param name="defaultWeighting">
    188.     ''' Thw weighting to be used for those values for which one Is Not explicitly provided.
    189.     ''' </param>
    190.     ''' <returns>
    191.     ''' A 32-bit signed integer that is greater than or equal to 0, and less than maxValue; that is, the range of return values ordinarily includes 0 but not maxValue. However, if maxValue equals 0, maxValue is returned.
    192.     ''' </returns>
    193.     ''' <remarks>
    194.     ''' Weightings are a proportion of the total of all weightings.  They are not percentages.
    195.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    196.     ''' </remarks>
    197.     <Extension>
    198.     Public Function NextWithWeighting(source As Random,
    199.                                       maxValue As Integer,
    200.                                       weightings As IDictionary(Of Integer, Integer),
    201.                                       defaultWeighting As Integer) As Integer
    202.         Return source.NextWithWeighting(0, maxValue, weightings, defaultWeighting)
    203.     End Function
    204.  
    205.     ''' <summary>
    206.     ''' Returns a random integer that is within a specified range where each value in that range has a weighted probability.
    207.     ''' </summary>
    208.     ''' <param name="source">
    209.     ''' The <see cref="Random"/> object to use to generate the number.
    210.     ''' </param>
    211.     ''' <param name="minValue">
    212.     ''' The inclusive lower bound of the random number returned.
    213.     ''' </param>
    214.     ''' <param name="maxValue">
    215.     ''' The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.
    216.     ''' </param>
    217.     ''' <param name="weightings">
    218.     ''' A dictionary containing the weightings for some Or all of the possible outcomes, where the keys are the possible outcomes And the values are the weightings.
    219.     ''' </param>
    220.     ''' <param name="defaultWeighting">
    221.     ''' Thw weighting to be used for those values for which one Is Not explicitly provided.
    222.     ''' </param>
    223.     ''' <returns>
    224.     ''' A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned.
    225.     ''' </returns>
    226.     ''' <remarks>
    227.     ''' Weightings are a proportion of the total of all weightings.  They are not percentages.
    228.     ''' For instance, if there are three possible outcomes and the weightings are 1, 2 and 3 then the first outcome will result in about 1/6 of the time, the second outcome will result about 1/3 of the time and the third outcome will result about 1/2 of the time.
    229.     ''' </remarks>
    230.     <Extension>
    231.     Public Function NextWithWeighting(source As Random,
    232.                                       minValue As Integer,
    233.                                       maxValue As Integer,
    234.                                       weightings As IDictionary(Of Integer, Integer),
    235.                                       defaultWeighting As Integer) As Integer
    236.         If minValue > maxValue Then
    237.             Throw New ArgumentOutOfRangeException($"'{NameOf(minValue)}' cannot be greater than {NameOf(maxValue)}.", NameOf(minValue))
    238.         End If
    239.  
    240.         If weightings.Keys.Any(Function(k) k < minValue OrElse
    241.                                            minValue = maxValue AndAlso k > maxValue OrElse
    242.                                            minValue < maxValue AndAlso k >= maxValue) Then
    243.             Throw New ArgumentException("Weightings cannot be provided for values outside the specified range.")
    244.         End If
    245.  
    246.         If defaultWeighting < 0 Then
    247.             Throw New ArgumentOutOfRangeException(NameOf(defaultWeighting), defaultWeighting, "Value cannot be less than zero.")
    248.         End If
    249.  
    250.         Dim weighting As Integer
    251.         Dim allWeightings As New List(Of Integer) From {If(weightings.TryGetValue(minValue, weighting),
    252.                                                            weighting,
    253.                                                            defaultWeighting)}
    254.  
    255.         For i = minValue + 1 To maxValue - 1 Step 1
    256.             allWeightings.Add(If(weightings.TryGetValue(i, weighting),
    257.                                  weighting,
    258.                                  defaultWeighting))
    259.         Next
    260.  
    261.         Return source.NextWithWeighting(minValue, maxValue, allWeightings.ToArray())
    262.     End Function
    263.  
    264. End Module
    The overloads of NextWithWeighting correspond to the overloads of Random.Next that have parameters. Like those Next overloads, the 'maxValue' parameter is exclusive while the 'minValue' parameter is inclusive. NextWithWeighting requires that a weighting is provided for every possible outcome. I plan to add overloads that allow you to provide weightings for just specific values and have a default for everything else.

    As an example, the following code generates random numbers that are greater than or equal to zero and less than 4. The possible outcomes of 0, 1, 2 and 3 have weightings of 1, 2, 3 and 4 respectively. That means that, for every 1 time that value 0 is generated, the value 1 should be generated twice, the value 2 should be generated three times and the value 3 should be generated four times.
    Code:
    Dim rng As New Random
    Dim results As New Dictionary(Of Integer, Integer) From {{0, 0}, {1, 0}, {2, 0}, {3, 0}}
    
    For i = 1 To 1000
        Dim number = rng.NextWithWeighting(4, {1, 2, 3, 4})
    
        results(number) += 1
    Next
    
    For Each kvp In results
        Console.WriteLine("{0} was generated {1} times.", kvp.Key, kvp.Value)
    Next
    If you run that code multiple times and check the output, you'll see that the specific results will vary but they are close to those proportions each time, i.e. 0 is generated about 100 out of 1000 times, 1 is generated about 200 times, 2 is generated about 300 times and 3 is generated about 400 times.

    The following code is similar but specifies a lower bound for the range and less regular weightings:
    Code:
    Dim rng As New Random
    Dim results As New Dictionary(Of Integer, Integer)
    
    For i = 8 To 13
        results.Add(i, 0)
    Next
    
    Dim weightings = {11, 5, 3, 21, 65, 26}
    
    For i = 1 To weightings.Sum() * 100
        Dim number = rng.NextWithWeighting(8, 14, weightings)
    
        results(number) += 1
    Next
    
    For Each kvp In results
        Console.WriteLine("{0} was generated {1} times.", kvp.Key, kvp.Value)
    Next
    Again, if you run that code multiple times, you should see that the proportions in which the values are generated is always approximately the same as specified by the weightings.

    Note that the weightings are not percentages and so they do not have to sum to 100. They are simply relative proportions. That means that weightings of 1, 2 and 3 are perfectly equivalent to weightings of 347, 694 and 1041.

  4. #4

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

    Useful Extension Methods for Dates

    vb.net Code:
    1. ' Gets a value indicating whether a <see cref="DateTime"/> value represents a week day.
    2. Public Function IsWeekDay(source As Date) As Boolean
    3. ' Returns a new <see cref="DateTime"/> that adds the specified number of week days to a specified value.
    4. Public Function AddWeekDays(source As Date, value As Double) As Date
    The methods in the DateTime project allow you to easily add a number of days to a Date while ignoring weekends.
    vb.net Code:
    1. Imports System.Runtime.CompilerServices
    2.  
    3. ''' <summary>
    4. ''' Contains methods that extend the <see cref="DateTime"/> structure.
    5. ''' </summary>
    6. Public Module DateTimeExtensions
    7.  
    8.     ''' <summary>
    9.     ''' Gets a value indicating whether a <see cref="DateTime"/> value represents a week day.
    10.     ''' </summary>
    11.     ''' <param name="source">
    12.     ''' The input <see cref="DateTime"/>, which acts as the <b>this</b> instance for the extension method.
    13.     ''' </param>
    14.     ''' <returns>
    15.     ''' <b>true</b> if the represents a week day; otherwise <b>false</b>.
    16.     ''' </returns>
    17.     ''' <remarks>
    18.     ''' All days other than Saturday and Sunday are considered week days.
    19.     ''' </remarks>
    20.     <Extension>
    21.     Public Function IsWeekDay(source As Date) As Boolean
    22.         Return source.DayOfWeek <> DayOfWeek.Saturday AndAlso
    23.                source.DayOfWeek <> DayOfWeek.Sunday
    24.     End Function
    25.  
    26.     ''' <summary>
    27.     ''' Returns a new <see cref="DateTime"/> that adds the specified number of week days to a specified value.
    28.     ''' </summary>
    29.     ''' <param name="source">
    30.     ''' The input <see cref="DateTime"/>, which acts as the <b>this</b> instance for the extension method.
    31.     ''' </param>
    32.     ''' <param name="value">
    33.     ''' A number of whole and fractional days. The <i>value</i> parameter can be negative or positive.
    34.     ''' </param>
    35.     ''' <returns>
    36.     ''' An object whose value is the sum of the date and time represented by this instance and the number of week days represented by <i>value</i>.
    37.     ''' </returns>
    38.     ''' <remarks>
    39.     ''' All days other than Saturday and Sunday are considered week days.
    40.     ''' </remarks>
    41.     <Extension>
    42.     Public Function AddWeekDays(source As Date, value As Double) As Date
    43.         'A unit will be +/- 1 day.
    44.         Dim unit = Math.Sign(value) * 1.0
    45.  
    46.         'Start increasing the date by units from the initial date.
    47.         Dim result = source
    48.  
    49.         'When testing for zero, allow a margin for precision error.
    50.         Do Until value.IsEquivalentTo(0.0, 0.0001)
    51.             If Math.Abs(value) < 1.0 Then
    52.                 'There is less than one full day to add so we need to see whether adding it will take us past midnight.
    53.                 Dim temp = result.AddDays(value)
    54.  
    55.                 If temp.Date = result.Date OrElse temp.IsWeekDay() Then
    56.                     'Adding the partial day did not take us into a weekend day so we're done.
    57.                     result = temp
    58.                     value = 0.0
    59.                 Else
    60.                     'Adding the partial day took us into a weekend day so we need to add another day.
    61.                     result = result.AddDays(unit)
    62.                 End If
    63.             Else
    64.                 'Add a single day.
    65.                 result = result.AddDays(unit)
    66.  
    67.                 If result.IsWeekDay() Then
    68.                     'Adding a day did not take us into a weekend day so we can reduce the remaining value to add.
    69.                     value -= unit
    70.                 End If
    71.             End If
    72.         Loop
    73.  
    74.         Return result
    75.     End Function
    76.  
    77. End Module
    The AddWeekDays method works in the same way as the Date.AddDays method in that it can take a positive or negative, whole or fractional number of days and create a new Date by adding that number of days to the original Date. The following code compares the two methods:
    Code:
    Dim startDate = #4/19/2018 11:00 AM#
    
    For i = 0.5 To 10.0 Step 0.5
        Dim endDate = startDate.AddDays(i)
    
        Console.WriteLine("{0:ddd, d MMM yyyy h:mm tt} + {1:0.0} days =      {2:ddd, d MMM yyyy h:mm tt}", startDate, i, endDate)
    
        endDate = startDate.AddWeekDays(i)
    
        Console.WriteLine("{0:ddd, d MMM yyyy h:mm tt} + {1:0.0} week days = {2:ddd, d MMM yyyy h:mm tt}", startDate, i, endDate)
    Next
    That code produces the following output:
    Code:
    Thu, 19 Apr 2018 11:00 AM + 0.5 days =      Thu, 19 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 0.5 week days = Thu, 19 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 1.0 days =      Fri, 20 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 1.0 week days = Fri, 20 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 1.5 days =      Fri, 20 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 1.5 week days = Fri, 20 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 2.0 days =      Sat, 21 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 2.0 week days = Mon, 23 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 2.5 days =      Sat, 21 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 2.5 week days = Mon, 23 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 3.0 days =      Sun, 22 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 3.0 week days = Tue, 24 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 3.5 days =      Sun, 22 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 3.5 week days = Tue, 24 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 4.0 days =      Mon, 23 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 4.0 week days = Wed, 25 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 4.5 days =      Mon, 23 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 4.5 week days = Wed, 25 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 5.0 days =      Tue, 24 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 5.0 week days = Thu, 26 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 5.5 days =      Tue, 24 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 5.5 week days = Thu, 26 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 6.0 days =      Wed, 25 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 6.0 week days = Fri, 27 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 6.5 days =      Wed, 25 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 6.5 week days = Fri, 27 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 7.0 days =      Thu, 26 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 7.0 week days = Mon, 30 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 7.5 days =      Thu, 26 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 7.5 week days = Mon, 30 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 8.0 days =      Fri, 27 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 8.0 week days = Tue, 1 May 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 8.5 days =      Fri, 27 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 8.5 week days = Tue, 1 May 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 9.0 days =      Sat, 28 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 9.0 week days = Wed, 2 May 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 9.5 days =      Sat, 28 Apr 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 9.5 week days = Wed, 2 May 2018 11:00 PM
    Thu, 19 Apr 2018 11:00 AM + 10.0 days =      Sun, 29 Apr 2018 11:00 AM
    Thu, 19 Apr 2018 11:00 AM + 10.0 week days = Thu, 3 May 2018 11:00 AM

  5. #5

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

    Useful Extension Methods for Enumerable Lists

    vb.net Code:
    1. ' Randomises the items in an enumerable list.
    2. Public Function Randomize(Of T)(source As IEnumerable(Of T)) As IEnumerable(Of T)
    The method in the Enumerable project allows you to order an enumerable list of items randomly.
    vb.net Code:
    1. Imports System.Runtime.CompilerServices
    2.  
    3. ''' <summary>
    4. ''' Contains methods that extend the <see cref="IEnumerable(Of T)"/> interface.
    5. ''' </summary>
    6. Public Module EnumerableExtensions
    7.  
    8.     ''' <summary>
    9.     ''' A random number generator.
    10.     ''' </summary>
    11.     Private rng As Random
    12.  
    13.     ''' <summary>
    14.     ''' Randomises the items in an emumerable list.
    15.     ''' </summary>
    16.     ''' <typeparam name="T">
    17.     ''' The type of the items in the list.
    18.     ''' </typeparam>
    19.     ''' <param name="source">
    20.     ''' The input list.
    21.     ''' </param>
    22.     ''' <returns>
    23.     ''' The items from the input list in random order.
    24.     ''' </returns>
    25.     <Extension>
    26.     Public Function Randomize(Of T)(source As IEnumerable(Of T)) As IEnumerable(Of T)
    27.         EnsureRandomInitialized()
    28.  
    29.         Return source.OrderBy(Function(o) rng.NextDouble())
    30.     End Function
    31.  
    32.     ''' <summary>
    33.     ''' Initialises the random number generator if it is not already.
    34.     ''' </summary>
    35.     Private Sub EnsureRandomInitialized()
    36.         rng = If(rng, New Random)
    37.     End Sub
    38.  
    39. End Module
    Note that this method cannot be used to randomise an array or collection in-place. If called on an object implementing IList(Of T), it doesn't not affect the order of the items in that list, but exposes those items as an enumerable list in random order. That list can be enumerated directly or used as the source for another array or collection. For example, you could use this method to simulate a lottery draw like so:
    Code:
    Dim allNumbers = Enumerable.Range(1, 40)
    Dim selectedNumbers = allNumbers.Randomize().Take(6)
    
    Console.WriteLine("Your winning lottery numbers tonight are: " & String.Join(", ", selectedNumbers))
    You might also use this method to shuffle a deck of cards:
    Code:
    Dim values = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King"}
    Dim suits = {"Hearts", "Clubs", "Diamonds", "Spades"}
    Dim allCards = From value In values
                   From suit In suits
                   Select $"{value} of {suit}"
    Dim deck = New Stack(Of String)(allCards.Randomize())
    Dim hand As New List(Of String)
    
    For i = 1 To 5
        hand.Add(deck.Pop())
    Next
    
    Console.WriteLine("Your hand contains: " + String.Join(", ", hand))

  6. #6

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

    Re: Useful Extension Methods

    Updated attachment. See edit to post #1.

  7. #7

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

    Re: Useful Extension Methods

    Updated attachment. See edit to post #1.

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