Question How to iterate through a List randomly and in order

Mynotoar

Active member
Joined
Sep 27, 2012
Messages
28
Location
England
Programming Experience
Beginner
Hey there!

I started VB just over a month ago, we've been working with console applications - no GUI - and I've been having great fun with it. Recently - with every bit of help from wonderful forumers - I experimented using Lists to create a Periodic Table quiz, and now I had some more questions about lists.

Firstly and most simply, how do you use ranges in lists? For example, say I wanted to create a list of the numbers 1-100. I've noticed scrolling through options that there is .AddRange in a With tag, but I can't work out how to use it - none of the arguments I've tried are valid.

Secondly, how do you pick a random value from a list? For example, say I wanted to write a simple lottery number program, and so I create a list with all the possible numbers, then I need to randomly choose a value from that list and repeat that six or seven times - how would I go about doing that?

Third and lastly, how do you compare two lists in order? For example, say I'm writing a program which has four values, "Up", "Down", "Left" or "Right". Then say I wanted to create a random sequence of up, down, left and right, e.g. "Left Up Down Up Down", which would be stored in a List(Of String) as the correct sequence. Then the user inputs a sequence of these four values, and this is entered into another List(Of String) as the user-given sequence. What I want to do then would be to compare the two strings to check that they match.

So to conclude, my three questions:
1. How do you include ranges in lists?
2. How do you pick a random value from a list?
3. How do you compare the values of two lists in order to make sure they match?

I hope you can help, thank you in advance :).
 
1. How do you include ranges in lists?
The most basic way to do that would be with a simple For loop, e.g.
Dim numbers As New List(Of Integer)

For number = 1 To 100
    numbers.Add(number)
Next
If you want to create range of Integer values though, you can simplify your code by using the Enumerable.Range method. That will return an IEnumerable(Of Integer), i.e. a list of Integers that you can enumerate with a For Each loop, and the AddRange method you speak of takes an IEnumerable as an argument, e.g.
Dim numbers As New List(Of Integer)
Dim range As IEnumerable(Of Integer) = Enumerable.Range(1, 100)

numbers.AddRange(range)
That will add a range to an existing List but you can simplify that further by using that range to create the List in the first place, e.g.
Dim numbers As List(Of Integer) = Enumerable.Range(1, 100).ToList()
2. How do you pick a random value from a list?
Whenever you want to do something random, it almost always means using the Random class. It has several methods to generate random numbers of different types and you can then use that number in whatever way is appropriate in your particular scenario. In your case, you would use the Next method to generate a random Integer and then use that as an index into your List to get the value at that index. There are a couple of points to note here. Firstly, you should pretty much never create multiple Random objects. If you need multiple random numbers then you create one instance and use it multiple times. Secondly, there's nothing inherent in the Random class that will prevent it repeating a value. You have to handle that part yourself. With that in mind, check out these two examples I created some time ago:

Unique, Random Selections from a List
(.NET 3.5) Randomise a List of Items
3. How do you compare the values of two lists in order to make sure they match?
You would first test the Count properties of the two Lists and, if they are not the same, you know that the Lists cannot contain the same items. If that have the same item count then you would use a For loop to go through the indexes, safe in the knowledge that the last index of each is the same. At each iteration, you can use the loop counter as an index into both Lists to get the pair of corresponding items and then simply compare them. You declare a variable before the loop and set it to True by default. If you find a pair of items that don't match then you would set that variable to False and Exit For out of the loop. At the end of the loop, that variable will tell you directly whether the two Lists contain the same items or not.
 
Hi Leo,

Can I please mention a word of caution with regards to the Random class. With still being a relative Newbie myself to the .NET Framework I still often come across .NET Class's which have superseded old functionality and today's new class for me is the Random class as introduced by jmcilhinney.

On initially seeing jmcilhinney's sample coding using the Random class I thought the code was incorrect (it is not, of course, jmcilhinney) because I assumed that Random.Next(0,List.Count) being Random.Next(0,10) would result in a random number between 0 and 10 and yet I knew that the index of the List would go from 0 to 9 so if you accessed the List with the random number 10 being item=List(10) then an error would occur since List(10) does not exist.

After looking into the Random class a bit further, and this is the important bit, I realised that the specified minValue parameter is inclusive of any random generation but the maxValue parameter is exclusive of any random number generation. i.e. myRand.Next(5,10) will return random numbers between 5,6,7,8 & 9 not 10.

One to remember for the future.

Cheers and thanks also jmcilhinney,

Ian
 
Thank you both! Though jmcil, I don't know how to use Count, and I can use a For loop but I don't know how to get it to iterate the way you specify :c.
 
3. How do you compare the values of two lists in order to make sure they match?
You would first test the Count properties of the two Lists and, if they are not the same, you know that the Lists cannot contain the same items. If that have the same item count then you would use a For loop to go through the indexes, safe in the knowledge that the last index of each is the same. At each iteration, you can use the loop counter as an index into both Lists to get the pair of corresponding items and then simply compare them. You declare a variable before the loop and set it to True by default. If you find a pair of items that don't match then you would set that variable to False and Exit For out of the loop. At the end of the loop, that variable will tell you directly whether the two Lists contain the same items or not.
Another option could be the SequenceEqual method.
Enumerable.SequenceEqual(TSource) Method (IEnumerable(TSource), IEnumerable(TSource)) (System.Linq)
 
Hi Leo,

To answer your question re jcmilhinney comments create a new form, add three buttons and then replace the code behind with the code below.

This demonstrates the principals that jcmilhinney explained. The only difference in each button's code is the information contained in the lists.

VB.NET:
Public Class Form1
 
  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim FirstList As List(Of Integer) = Enumerable.Range(1, 100).ToList
    Dim SecondList As List(Of Integer) = Enumerable.Range(1, 90).ToList
    Dim ListsMatch As Boolean = True
    Dim ListIndex As Integer
 
    If Not FirstList.Count = SecondList.Count Then
      ListsMatch = False
    Else
      'dont forget that lists are indexed from 0
      For ListIndex = 0 To FirstList.Count - 1
        If Not FirstList(ListIndex) = SecondList(ListIndex) Then
          ListsMatch = False
          Exit For
        End If
      Next
    End If
    MsgBox(IIf(ListsMatch, "The Two Lists Match", "The Two Lists Do Not Match"))
  End Sub
 
  Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    Dim FirstList As List(Of Integer) = Enumerable.Range(1, 100).ToList
    Dim SecondList As List(Of Integer) = Enumerable.Range(1, 100).ToList
    Dim ListsMatch As Boolean = True
    Dim ListIndex As Integer
 
    If Not FirstList.Count = SecondList.Count Then
      ListsMatch = False
    Else
      'dont forget that lists are indexed from 0
      For ListIndex = 0 To FirstList.Count - 1
        If Not FirstList(ListIndex) = SecondList(ListIndex) Then
          ListsMatch = False
          Exit For
        End If
      Next
    End If
    MsgBox(IIf(ListsMatch, "The Two Lists Match", "The Two Lists Do Not Match"))
  End Sub
 
  Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    Dim FirstList As List(Of Integer) = Enumerable.Range(1, 100).ToList
    Dim SecondList As List(Of Integer) = Enumerable.Range(101, 100).ToList
    Dim ListsMatch As Boolean = True
    Dim ListIndex As Integer
 
    If Not FirstList.Count = SecondList.Count Then
      ListsMatch = False
    Else
      'dont forget that lists are indexed from 0
      For ListIndex = 0 To FirstList.Count - 1
        If Not FirstList(ListIndex) = SecondList(ListIndex) Then
          ListsMatch = False
          Exit For
        End If
      Next
    End If
    MsgBox(IIf(ListsMatch, "The Two Lists Match", "The Two Lists Do Not Match"))
  End Sub
End Class
Then you have got JohnH's solution to explore and learn. Good Luck.

Cheers,

Ian
 
Hey guys, sorry I haven't been as active in this topic or letting you know how I was getting along - I admit I got bored of the number-list problem before I could really develop a program out of it. I have another issue though, which I'll post in a new thread.
 
Hey guys,

So I worked on this a bit and with mostly jmcil's advice I managed to get a Lottery program to work :). Took me long enough, but hey! It doesn't do it quite the way I originally planned it, but it still works and it's good enough for me. Thanks all!

VB.NET:
Module LotteryNumbers
    Dim LotteryNumbers As New List(Of Integer)
    Dim LotterySelection As New List(Of Integer)
    Dim RandomElement As New Random
    Dim Keypress As String
    Dim Index As Integer
    Dim BallNumber As Integer
    Dim BallSelection As Integer
    Dim CorrectBalls As Integer
    Dim AcceptInput As Boolean
    Dim CorrectPowerBall As Boolean
    Dim Quit As Boolean
    Sub Main()
        Do
            Console.Clear()
            LotteryNumbers.Clear()
            LotterySelection.Clear()
            PopulateLotteryNumbers()
            InputLotterySelection()
            Console.WriteLine()
            GenerateLotteryNumbers()
            CheckCorrectBalls()
            RepeatProgram()
        Loop Until Quit = True
    End Sub
    Private Sub PopulateLotteryNumbers()
        For i = 0 To 49
            LotteryNumbers.Add(i)
        Next
    End Sub
    Private Sub InputLotterySelection()
        Console.WriteLine("Please select 7 numbers between 1 and 49!")
        For i = 1 To 6
            Console.Write("Ball " & i & " - ")
            Do
                Console.ForegroundColor = ConsoleColor.Cyan
                BallSelection = Console.ReadLine
                Console.ResetColor()
                If LotterySelection.Contains(BallSelection) Then
                    Console.ForegroundColor = ConsoleColor.Red
                    Console.WriteLine("You have already selected " & BallSelection & ". Enter another number.")
                    Console.ResetColor()
                    Console.Write("Ball " & i & " - ")
                    AcceptInput = False
                Else


                    AcceptInput = True
                End If
            Loop Until AcceptInput = True
            Console.ResetColor()
            LotterySelection.Add(BallSelection)
        Next
        Console.Write("PowerBall - ")
        Do
            Console.ForegroundColor = ConsoleColor.Cyan
            BallSelection = Console.ReadLine
            Console.ResetColor()
            If LotterySelection.Contains(BallSelection) Then
                Console.ForegroundColor = ConsoleColor.Red
                Console.WriteLine("You have already selected " & BallSelection & ". Enter another number.")
                Console.ResetColor()
                Console.Write("PowerBall - ")
                AcceptInput = False
            Else


                AcceptInput = True
            End If
        Loop Until AcceptInput = True
        Console.ResetColor()
        LotterySelection.Add(BallSelection)
    End Sub
    Private Sub GenerateLotteryNumbers()
        Console.WriteLine("The Lottery Balls are...")
        For i = 1 To 400000000


        Next
        For i = 1 To 6
            Index = RandomElement.Next(0, LotteryNumbers.Count)
            BallNumber = LotteryNumbers(Index)
            LotteryNumbers.RemoveAt(Index)
            Console.Write("Ball " & i & " - ")
            If LotterySelection.Contains(BallNumber) Then
                Console.ForegroundColor = ConsoleColor.Green
                CorrectBalls += 1
            End If
            Console.WriteLine(BallNumber)
            Console.ResetColor()
            For j = 1 To 300000000


            Next
        Next
        Index = RandomElement.Next(0, LotteryNumbers.Count)
        BallNumber = LotteryNumbers(Index)
        LotteryNumbers.RemoveAt(Index)
        Console.Write("And the PowerBall is")
        For i = 1 To 3
            Console.Write(".")
            For j = 1 To 200000000


            Next
        Next
        Console.Write(" ")
        For i = 1 To 200000000


        Next
        If LotterySelection.Contains(BallNumber) Then
            Console.ForegroundColor = ConsoleColor.Green
            CorrectPowerBall = True
            CorrectBalls += 1
        End If
        Console.WriteLine(BallNumber)
        Console.ResetColor()
    End Sub
    Private Sub CheckCorrectBalls()
        Console.WriteLine()
        Console.Write("You guessed ")
        Console.ForegroundColor = ConsoleColor.Green
        Console.Write(CorrectBalls)
        Console.ResetColor()
        Console.Write(" balls correctly")
        If CorrectPowerBall = True Then
            Console.WriteLine(", and you guessed the PowerBall correctly too!")
        Else
            Console.WriteLine(".")
        End If
        Select Case CorrectBalls
            Case Is = 0
                Console.ForegroundColor = ConsoleColor.Red
                Console.WriteLine("Too bad.")
                Console.ResetColor()
            Case 1 To 2
                Console.WriteLine("Not bad.")
            Case 3 To 5
                Console.WriteLine("Well done!")
            Case 6
                Console.WriteLine("That's amazing!")
            Case 7
                Console.WriteLine("Congratulations! You guessed every single number correctly!")
        End Select
    End Sub
    Private Sub RepeatProgram()
        Console.WriteLine()
        Console.WriteLine("(1) Play again  (2) Quit")
        Keypress = Console.ReadKey(True).KeyChar
        If Keypress = Chr(49) Then
            Quit = False
        ElseIf Keypress = Chr(50) Then
            Quit = True
        End If
    End Sub
End Module

Everything is divided into procedures - the first procedure, PopulateLotteryNumbers, populates the list of Lottery numbers from which the program has to choose (which is reset every time.) The second procedure, InputLotterySelection gets the user to put in seven numbers, with something that stops you from entering repeat numbers and asks you to enter it again if you do. The third procedure, GenerateLotteryNumbers, generates 7 random numbers selected from the list, highlighting in green every matched value you got and adding it to a value (CorrectBalls). The fourth procedure, CheckCorrectBalls, evaluates how many CorrectBalls you have - how many numbers you guessed that match to the real thing - and gives you a Case response based on that. Finally, the fifth procedure is a simple If statement that asks if you want to loop.

Really happy with this! :D
 
Hi Leo,

It works well, so well done on learning and implementing some of the principals that have been suggested to you in the forum.

It feels really good when you get something right for the first time does it not?

OK, now the compliments are over for a good effort, would you like some constructive criticism?

Cheers,

Ian
 
Hi Leo,

Ok here are some points for you to consider when creating your programs:-

1) When defining a range of values to be used in a routine it's important that you use the right values. In your PopulateLotteryNumbers sub what's wrong with for i = 0 to 49? What lottery numbers can actually be drawn?

2) When using local variables in a routine it's always good practice to declare your local variables with qualifying names before you use them. You use i numerous times in your subs but what does i mean? This is a small routine so it's all self explanatory but for larger applications you will appreciate the beauty of doing this. i.e in InputLotterySelection you use for i = 1 to 6, you could have better said Dim SelectedLotteryNumbers as integer. For SelectedLotteryNumbers = 1 to 6.

3) In your InputLotterySelection routine you have this same section of code twice:-

VB.NET:
Do
        Console.ForegroundColor = ConsoleColor.Cyan
        BallSelection = CInt(Console.ReadLine)
        Console.ResetColor()
        If LotterySelection.Contains(BallSelection) Then
          Console.ForegroundColor = ConsoleColor.Red
          Console.WriteLine("You have already selected " & BallSelection & ". Enter another number.")
          Console.ResetColor()
          Console.Write("Ball " & i & " - ")
          AcceptInput = False
        Else
          AcceptInput = True
        End If
      Loop Until AcceptInput = True
      Console.ResetColor()
      LotterySelection.Add(BallSelection)
You do this once for the lottery numbers and then again for the Powerball number. Up until now you have only used Subs but this is where you need to learn functions so that you can place this one block of code in a function and then call it many times and return the number generated by the function.

4) You use For i = 1 To 400000000 Next to pause your application to simulate "thinking". A much better way to do think is to use Threading.Thread.Sleep(1000) - the value is in milliseconds and therefore the application sleeps for 1 second.

5) In the GenerateLotteryNumbers sub what's wrong with RandomElement.Next(0, LotteryNumbers.Count) - This is directly related to point 1. - Just had a shower and realized that this comment from me is incorrect. It's actually fine but can you see why?

6) When you run the application. Try pressing the return key without entering a number and see what happens. How can you fix this? In addition to this try entering any non-numeric value and see what happens. Same issue. To finish this point try entering the number 89. Should this be allowed or should you consider some form of data entry qualification?

7) I have found one logic error so far. Run the application and keep playing numerous times. Keep a watch on your variables which count the correct numbers and the Powerball number. What's wrong with these?

8) You have defined all your variables with their scope available to the whole application. This is not good practice. Take the BallSelection variable for example. Where do you use this variable. From a best practice point of view where should this variable be declared? You should consider this question for all your variables.

I think that's about it. Please take my comments as constructive criticism since the sign of a good up and coming programmer is one that can take criticism, learn from it and apply the results effectively.

Good luck and cheers,

Ian
 
Last edited:
Back
Top