summing elements of a .txt file array in VB.net - arrays

College student in an advanced VB class who is turning to a forum for help - I've found a few examples of code but am having a hard time figuring this one out.. any and all help is appreciated :)
This application imports a .txt file stored in the bin, debug folder called data.txt
..20 records, 3 lines per record, the last line is the student's grade, I need to average the grades by summing each records grade and dividing by 20 and then displaying on the lstbox showing the average.
So far I've got..
Dim objReader As IO.StreamReader
Dim intFill As Integer
Dim intCount As Integer = 0
Dim intAverage As Integer
Dim strLocationAndNameOfFile As String = "data.text"
If IO.File.Exists(strLocationAndNameOfFile) = True Then
objReader = IO.File.OpenText(strLocationAndNameOfFile)
Else
MsgBox("The file is not available. Restart the program when the file is avilable", , "Error")
Me.Close()
End If
If IO.File.Exists(strLocationAndNameOfFile) Then
objReader = IO.File.OpenText(strLocationAndNameOfFile)
Do While objReader.Peek <> -1
_strName(intCount) = Convert.ToString(objReader.ReadLine())
_strItemID(intCount) = Convert.ToString(objReader.ReadLine())
_intGrade(intCount) = Convert.ToInt32(objReader.ReadLine())
intCount += 1
Loop
objReader.Close()
End If
For intFill = 0 To (_strName.Length - 1)
*'intAverage = SUM OF ALL AVERAGES / LENGTH OF ARRAY -1*
Me.lstAverage.Items.Add(intAverage.ToString())

While looping to read the grades sum them up
Dim total as Integer
Do While objReader.Peek <> -1
_strName(intCount) = Convert.ToString(objReader.ReadLine())
_strItemID(intCount) = Convert.ToString(objReader.ReadLine())
_intGrade(intCount) = Convert.ToInt32(objReader.ReadLine())
total += _intGrade(intCount)
intCount += 1
Loop
And then just divide by 20 or _intGrade.Length
intAverage = total / _intGrade.Length

So many issues, as much as I loathe doing other's homework I want you to see what this could look like
Public Function GetAverageGrade(ByVal filename As String) As Double
Dim totalGrade As Integer = 0
Dim lineCount As Integer = 0
Dim line As String
Using rdr As New IO.StreamReader(filename)
While (line = rdr.ReadLine()) IsNot Nothing
lineCount += 1
If lineCount Mod 3 = 0 Then totalGrade += Convert.ToInt32(line)
End While
End Using
Return totalGrade / (lineCount / 3.0)
End Function
Of course, you probably want to do more with that data than just get the average grade. So an even better option is build code that reads it all in as a set of records:
Public Class GradeItem
Public Property Name As String
Public Property Item As String
Public Property Grade As Integer
End Class
and then
Public Iterator Function ReadGradeItems(ByVal fileName As String) As IEnumerable(Of GradeItem)
Using rdr As New IO.StreamReader(fileName)
While rdr.Peek() <> -1
Yield New GradeItem With {.Name = rdr.ReadLine(), .Item= rdr.ReadLine(), .Grade = Convert.ToInt32(rdr.ReadLine()}
End While
End Using
End Function
and now put it all together:
Dim grades As IEnumerable(Of GradeItem) = ReadGradeItems("data.text")
lstAverage.Items.Add(grades.Select(Function(g) g.Grade).Average())

Related

VB.net Splitting a 1D array down into a 2D array

I am currently writing a (multiple choice) quiz program using VB.net I have read the answer options in from a .txt file into a single dimension temporary array however I would like to split this down into each question and the answer options for each questions into a 2D array so that (0,0) would be option A for question one and then (0,1) would be Option B for question 1 etc and then (1,0) would be option A for question 2. Below is the method I have tried however I get the error: "Object reference not set to an instance of an object" when adding 1 to a variable when I try and run this section of the code optnum = optnum + 1 Any help would be greatly appreciated, either fixing my code below or suggesting another method.
Dim optnum As Integer
Dim tempq As Integer = 0
Gameload(Phase_3.sounds)
Questionnum = 0
L_start.Hide()
optnum = 0
'splits the temp array down into options for each question
For i = 0 To 39
questions(tempq, optnum) = temparray(i)
optnum = optnum + 1
'there are 4 options for each question
'moves on to the next question when there is 4 options in the question
If optnum = 3 Then
tempq = tempq + 1
optnum = 0
End If
Next
For i = 0 To 3
L_option1.Text = questions(0, i)
Next
question_set()
Edit:
Here is the new full code I am still getting the error: Object reference not set to an instance of an object but now at the next in this section of code.
'''
For optnum = 0 To 3
questions(i, optnum) = temparray(i * 4 + optnum)
Next
'''
Thank you for all the help so far
'''
Public Class Game
Dim submission As Integer
Dim Correct_ans As Integer
Dim temparray() As String
Dim questions(,) As String
Dim Questionnum As Integer
Dim rs As New Resizer
Private Sub Game_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'finds all components on the screen in preperation if the screen resizes
rs.FindAllControls(Me)
L_start.Show()
End Sub
Private Sub Game_Resize(sender As Object, e As EventArgs) Handles Me.Resize
'resizes all components on the screen to same proportions
rs.ResizeAllControls(Me)
End Sub
Sub Gameload(ByVal sounds As String)
Dim pack As String
'reads in the sound pack
pack = My.Resources.ResourceManager.GetString(sounds)
Phase_3.Close()
'splits the pack into an array so that it can be broken down into questions
temparray = pack.Split(","c)
End Sub
Sub L_start_Click(sender As Object, e As EventArgs) Handles L_start.Click
Dim optnum As Integer
Dim tempq As Integer = 0
Gameload(Phase_3.sounds)
Questionnum = 0
L_start.Hide()
optnum = 0
'splits the temp array down into options for each question
For i = 0 To temparray.Count / 4
For optnum = 0 To 3
questions(i, optnum) = temparray(i * 4 + optnum)
Next
Next
For i = 0 To 3
L_option1.Text = questions(0, i)
Next
End Sub
'''
This is just my opinion but I think your aproach would be hard to maintain. I never saw the value of a class until I actually tried one. Maybe this is your chance.
Public Class Quiz
Public Property QuestionNumber As Integer
Public Property Question As String
Public Property AnswerA As String
Public Property AnswerB As String
Public Property AnswerC As String
Public Property AnswerD As String
Public Property CorrectAnswer As String
Public Overrides Function ToString() As String
Return $"Question: {QuestionNumber}.{Question} Answer Choices: {AnswerA}, {AnswerB}, {AnswerC}, {AnswerD} Correct Answer - {CorrectAnswer}"
End Function
End Class
To use your class...
Private QuestionList As New List(Of Quiz)
Private Sub OPCode2()
Dim temparray = File.ReadAllLines("answers.txt")
Dim temparraylocation As Integer
Dim questions = File.ReadAllLines("questions.txt")
Dim correctAnswers = File.ReadAllLines("correct.txt")
For i = 0 To questions.Length - 1
Dim qu As New Quiz
qu.QuestionNumber = i + 1
qu.Question = questions(i)
qu.CorrectAnswer = correctAnswers(i)
qu.AnswerA = temparray(temparraylocation)
temparraylocation += 1
qu.AnswerB = temparray(temparraylocation)
temparraylocation += 1
qu.AnswerC = temparray(temparraylocation)
temparraylocation += 1
qu.AnswerD = temparray(temparraylocation)
temparraylocation += 1
QuestionList.Add(qu)
Next
For Each q In QuestionList
Debug.Print(q.ToString)
Next
End Sub
Where's your declaration for temparray and questions. Will the quiz always have 4 choices and 10 questions?
It might be easier for you to have a nested loop so that i is temparray.count/4 and optnum is 0-3, then you can populate questions(i, optnum) = temparray(i*4+optnum)
Looking at your new code, you haven't initialised the questions variable. Only declared it.
Personally I would change questions to a list(of question), then use Mary's answer.

Need Help properly displaying an array in a listbox

Public Class Form1
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim Teams() As String = IO.File.ReadAllLines("SBWinners.txt")
Dim Team As String
Dim SteelersWins As Integer = 0
Dim RaidersWins As Integer = 0
Dim PackersWins As Integer = 0
Dim CowboysWins As Integer = 0
Dim GiantsWins As Integer = 0
Dim RamsWins As Integer = 0
Dim RavensWins As Integer = 0
Dim SaintsWins As Integer = 0
Dim FortyNinersWins As Integer = 0
Dim RedskinsWins As Integer = 0
Dim BroncosWins As Integer = 0
Dim PatriotsWins As Integer = 0
Dim ColtsWins As Integer = 0
Dim DolphinsWins As Integer = 0
Dim BearsWins As Integer = 0
Dim JetsWins As Integer = 0
Dim ChiefsWins As Integer = 0
Dim BuccWins As Integer = 0
For Each team In Teams
If team = "Steelers" Then
SteelersWins += 1
End If
If team = "Raiders" Then
RaidersWins += 1
End If
If team = "Packers" Then
PackersWins += 1
End If
If team = "Cowboys" Then
CowboysWins += 1
End If
If Team = "Giants" Then
GiantsWins += 1
End If
If team = "Rams" Then
RamsWins += 1
End If
If team = "Ravens" Then
RavensWins += 1
End If
If team = "Saints" Then
SaintsWins += 1
End If
If team = "Forty-Niners" Then
FortyNinersWins += 1
End If
If team = "Redskins" Then
RedskinsWins += 1
End If
If team = "Broncos" Then
BroncosWins += 1
End If
If team = "Patriots" Then
PatriotsWins += 1
End If
If team = "Colts" Then
ColtsWins += 1
End If
If team = "Dolphins" Then
DolphinsWins += 1
End If
If team = "Bears" Then
BearsWins += 1
End If
If team = "Jets" Then
JetsWins += 1
End If
If Team = "Chiefs" Then
ChiefsWins += 1
End If
If team = "Buccaneers" Then
BuccWins += 1
End If
Next
Dim Wins() As Integer = {SteelersWins, RaidersWins, PackersWins, CowboysWins, GiantsWins, RamsWins, RavensWins, SaintsWins, FortyNinersWins, RedskinsWins,
BroncosWins, PatriotsWins, ColtsWins, DolphinsWins, BearsWins, JetsWins, ChiefsWins, BuccWins}
For Each win In Wins
Array.Sort(Wins)
Array.Reverse(Wins)
lstOutput.Items.Add(win)
Next
End Sub
End Class
What I have right now is code that reads a text file with the names of Superbowl winners, and counts the number of wins by the number of times the team name appears. These wins are then put into an array and sorted in descending order and displayed in a listbox. This all works fine.
My problem is that I need to display the corresponding team name with their number of wins on the same line in the listbox. So, instead of being:
6
5
5
4
4
It needs to be:
6 Steelers
5 49ers
5 Cowboys
4 Giants
4 Packers
And so on.
There are a couple of issues with your code, not the least of which is you are using hard coded names and variables to collect your team information. This is severely limiting if new names are added or names change.
As Plutonix mentioned in the comments you need to use a class to properly identify and collate your information. I also strongly suggest you use a List(of T) collection to contain your data.
The following code does what you desire.
Private Class cls_Team_wins
Public Team_Name As String
Public Wins As Integer
Public Sub New(Name As String)
Team_Name = Name
Wins = 1
End Sub
Public Overrides Function ToString() As String
Return Strings.Right(" " & Wins, 2) & " - " & Team_Name
End Function
End Class
Private Sub btnCalculate_Click(sender As Object, e As EventArgs) Handles btnCalculate.Click
Dim Games = New List(Of cls_Team_wins)
Dim Teams() As String = IO.File.ReadAllLines("SBWinners.txt")
For Each Team As String In Teams
If Team <> "" Then
Dim Team_Win As cls_Team_wins = Games.Find(Function(x) x.Team_Name = Team)
If Team_Win Is Nothing Then
Games.Add(New cls_Team_wins(Team))
Else
Team_Win.Wins += 1
End If
End If
Next
Games.Sort(Function(x, y) -x.Wins.CompareTo(y.Wins))
ListBox1.DataSource = Games
End Sub
The class included the team name and win counter. It over-rides the ToString method so the list box knows what to show as text. The New function requires you pass the team name and presets the win counter to one.
The For Loop checks for and ignores blank team names, this can be an issue reading text files, esp on the last line. Otherwise it will either add a new team to the list if it does not already exist, or it will increment the win counter if it does.
Once the list is built it is reverse sorted (Note the minus sign in the "Function" compare statement, that's a little trick that reverses the sort), and finally given to the listbox as a datasource.
Good luck to you :)

Check if array contains another array

I'm searching a way to check if an array contains all the elements of another array.
This is the situation: I have two bytes arrays Bytes(): one contains the bytes of a file, and another contains the bytes to compare.
For example, if the file contains these bytes: 4D 5A 90 00 03 and the string to compare is 00 03, I want the function to return true. Else it will obviously return false. So, all bytes in the string to compare must be present in the file too.
I've already searched on the web for this. Tried the old good Contains() function, but for arrays it works only to compare a single byte. You know, one byte only is too little to identify a file!
If possible, I'd like to do this as fast as possible.
I'm working in VB.NET WinForms, VS 2013, .NET 4.5.1
Thanks in advance,
FWhite
EDIT:
Now I have a List(Of Bytes()) like this:
00 25 85 69
00 41 52
00 78 96 32
These are three Bytes() arrays. How do I check if my file bytes array contains all of these values (the file must contains 00 25 85 69, 00 41 52 and 00 78 96 32? I've tried with this code, but it doesn't work:
Dim BytesToCompare As List(Of Byte()) = StringToByteArray(S.Split(":")(3))
For Each B As Byte() In BytesToCompare
If FileBytes.All(Function(c) B.Contains(c)) Then
'Contains
TempResults.Add("T")
Else
TempResults.Add("F")
End If
Next
If CountResults(TempResults) Then
Return S
Exit For
End If
The code in CountResults is this:
Public Function CountResults(Input As List(Of String)) As Boolean
Dim TrueCount As Integer = 0
Dim FalseCount As Integer = 0
Dim TotalCount As Integer = Input.Count
For Each S In Input
If S = "T" Then
TrueCount = TrueCount + 1
ElseIf S = "F" Then
FalseCount = FalseCount + 1
End If
Next
If TrueCount = TotalCount Then
Return True
ElseIf FalseCount > TrueCount Then
Return False
End If
End Function
Tell me if you didn't understand and I'll try to better explain.
Thank you,
FWhite
I was thinking that maybe something other than the brute-force method would work and discovered the Boyer-Moore search algorithm. Shamelessly translating the C and Java code found at Boyer–Moore string search algorithm into VB.NET, I arrived at
Public Class BoyerMooreSearch
' from C and Java code at http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm
Private Shared Function SuffixLength(needle As Byte(), p As Integer) As Integer
Dim len As Integer = 0
Dim j = needle.Length - 1
Dim i = 0
While i >= 0 AndAlso needle(i) = needle(j)
i -= 1
j -= 1
len += 1
End While
Return len
End Function
Private Shared Function GetOffsetTable(needle As Byte()) As Integer()
Dim table(needle.Length - 1) As Integer
Dim lastPrefixPosition = needle.Length
For i = needle.Length - 1 To 0 Step -1
If Isprefix(needle, i + 1) Then
lastPrefixPosition = i + 1
End If
table(needle.Length - 1 - i) = lastPrefixPosition - i + needle.Length - 1
Next
For i = 0 To needle.Length - 2
Dim slen = SuffixLength(needle, i)
table(slen) = needle.Length - 1 - i + slen
Next
Return table
End Function
Private Shared Function Isprefix(needle As Byte(), p As Integer) As Boolean
Dim j = 0
For i = p To needle.Length - 1
If needle(i) <> needle(j) Then
Return False
End If
j += 1
Next
Return True
End Function
Private Shared Function GetCharTable(needle As Byte()) As Integer()
Const ALPHABET_SIZE As Integer = 256
Dim table(ALPHABET_SIZE - 1) As Integer
For i = 0 To table.Length - 1
table(i) = needle.Length
Next
For i = 0 To needle.Length - 2
table(needle(i)) = needle.Length - 1 - i
Next
Return table
End Function
Shared Function IndexOf(haystack As Byte(), needle As Byte()) As Integer
If needle.Length = 0 Then
Return 0
End If
Dim charTable = GetCharTable(needle)
Dim offsetTable = GetOffsetTable(needle)
Dim i = needle.Length - 1
While i < haystack.Length
Dim j = needle.Length - 1
While j >= 0 AndAlso haystack(i) = needle(j)
i -= 1
j -= 1
End While
If j < 0 Then
Return i + 1
End If
i += Math.Max(offsetTable(needle.Length - 1 - j), charTable(haystack(i)))
End While
Return -1
End Function
End Class
And to test it (suspecting that the LINQ code presented by #OneFineDay would demolish it for performance):
Imports System.IO
Imports System.Text
Module Module1
Dim bytesToCheck As List(Of Byte())
Dim rand As New Random
Function GetTestByteArrays() As List(Of Byte())
Dim testBytes As New List(Of Byte())
' N.B. adjust the numbers used in CreateTestFile according to the quantity (e.g. 10) of testData used
For i = 1 To 10
testBytes.Add(Encoding.ASCII.GetBytes("ABCDEFgfdhgf" & i.ToString() & "sdfgjdfjFGH"))
Next
Return testBytes
End Function
Sub CreateTestFile(f As String)
' Make a 4MB file of test data
' write a load of bytes which are not going to be in the
' judiciously chosen data to search for...
Using fs As New FileStream(f, FileMode.Create, FileAccess.Write)
For i = 0 To 2 ^ 22 - 1
fs.WriteByte(CByte(rand.Next(128, 256)))
Next
End Using
' ... and put the known data into the test data
Using fs As New FileStream(f, FileMode.Open)
For i = 0 To bytesToCheck.Count - 1
fs.Position = CLng(i * 2 ^ 18)
fs.Write(bytesToCheck(i), 0, bytesToCheck(i).Length)
Next
End Using
End Sub
Sub Main()
' the byte sequences to be searched for
bytesToCheckFor = GetTestByteArrays()
' Make a test file so that the data can be inspected
Dim testFileName As String = "C:\temp\testbytes.dat"
CreateTestFile(testFileName)
Dim fileData = File.ReadAllBytes(testFileName)
Dim sw As New Stopwatch
Dim containsP As Boolean = True
sw.Reset()
sw.Start()
For i = 0 To bytesToCheckFor.Count - 1
If BoyerMooreSearch.IndexOf(fileData, bytesToCheckFor(i)) = -1 Then
containsP = False
Exit For
End If
Next
sw.Stop()
Console.WriteLine("Boyer-Moore: {0} in {1}", containsP, sw.ElapsedTicks)
sw.Reset()
sw.Start()
Dim temp As New List(Of Byte)
Array.ForEach(bytesToCheckFor.ToArray, Sub(byteArray) Array.ForEach(byteArray, Sub(_byte) temp.Add(_byte)))
Dim result = fileData.All(Function(_byte) temp.Contains(_byte))
sw.Stop()
Console.WriteLine("LINQ: {0} in {1}", result, sw.ElapsedTicks)
Console.ReadLine()
End Sub
End Module
Now, I know that the byte sequences to match are in the test file (I confirmed that by using a hex editor to search for them) and, assuming (oh dear!) I am using the other method correctly, the latter doesn't work whereas mine does:
Boyer-Moore: True in 23913
LINQ: False in 3224
I did also test the first code example by OneFineDay for searching for small vs large patterns to match, and for less than seven or eight bytes that code was faster than Boyer-Moore. So, if you would care to test it for the size of data you're searching in and the size of the patterns you're looking for, Boyer-Moore might be a better fit to your "If possible, I'd like to do this as fast as possible."
EDIT
Further to the OP's uncertainty as to whether or not my suggested method works, here is a test with very small sample data:
Sub Test()
bytesToCheckFor = New List(Of Byte())
bytesToCheckFor.Add({0, 1}) ' the data to search for
bytesToCheckFor.Add({1, 2})
bytesToCheckFor.Add({0, 2})
Dim fileData As Byte() = {0, 1, 2} ' the file data
' METHOD 1: Boyer-Moore
Dim containsP As Boolean = True
For i = 0 To bytesToCheckFor.Count - 1
If BoyerMooreSearch.IndexOf(fileData, bytesToCheckFor(i)) = -1 Then
containsP = False
Exit For
End If
Next
Console.WriteLine("Boyer-Moore: {0}", containsP)
' METHOD 2: LINQ
Dim temp As New List(Of Byte)
Array.ForEach(bytesToCheckFor.ToArray, Sub(byteArray) Array.ForEach(byteArray, Sub(_byte) temp.Add(_byte)))
Dim result = fileData.All(Function(_byte) temp.Contains(_byte))
Console.WriteLine("LINQ: {0}", result)
Console.ReadLine()
End Sub
Outputs:
Boyer-Moore: False
LINQ: True
Also, I renamed the variables in my original Main() method to hopefully make them more meaningful.
You can use the All function to check for that. It returns a Boolean.
Dim orgByteArray() As Byte = {CByte(1), CByte(2), CByte(3)}
Dim testByteArray() As Byte = {CByte(1), CByte(2)}
Dim result = orgByteArray.All(Function(b) testByteArray.Contains(b))
'output for this case returns False
For comparing a List(Of Byte()) to a Byte() where the Byte() is the complte list of all sub arrays in the List(Of byte()).
Dim filebytes() As Byte = {CByte(1), CByte(2), CByte(3), CByte(3), CByte(4), CByte(5), CByte(6), CByte(7), CByte(8)}
Dim bytesToCheck As New List(Of Byte())
bytesToCheck.Add(New Byte() {CByte(1), CByte(2), CByte(3)})
bytesToCheck.Add(New Byte() {CByte(3), CByte(4), CByte(5)})
bytesToCheck.Add(New Byte() {CByte(6), CByte(7), CByte(8)})
Dim temp As New List(Of Byte)
Array.ForEach(bytesToCheck.ToArray, Sub(byteArray) Array.ForEach(byteArray, Sub(_byte) temp.Add(_byte)))
Dim result = filebytes.All(Function(_byte) temp.Contains(_byte))
'output = True

Grade Array Averager troubles

I am currently in an IT curriculum in college. Advance Visual Basic 2010 is a requirement however, I am not a programmer. I have been struggling to find my way through VB but this last assignment has me stumped.I am able to get the first name into the array and the 5 grades for that name . At that point, the loop will continue to ask for the next name and that names 5 grades and so on until the 4th name and grades are entered and then it should display all 4 names and grade averages in the listbox.
Here is the assignment...
Write a program that will input four students’ names and average five test grades for each student. The program should have an array for the students name and then a two-dimensional array for all their grades.
Your program should ask for the students name and then five test scores for that student.
Create a method that does the averaging and pass the arrays to that method. That method can also output the student name and average in a list box.
Call a method to figure up the average once you get all the grades. Do not figure it up as you get the information!! You’ll get a big ole zero if you do! Then have that same method output the results into the list box:
After 4 days of struggling with this, here is what I have come up with so far. Any guidance is greatly appreciated. Thank you in advance.
Public Class Form1
Private Sub btnNames_Click(sender As System.Object, e As System.EventArgs) Handles btnNames.Click
Dim NamesList(3) As String
Dim GradeArray(4) As Integer
Dim x As Integer
Dim y As Integer
Dim Sum As Integer
Dim Avg As Integer
For y = 0 To NamesList(3)
NamesList(x) = InputBox("Enter student number " & y + 1 & "'s name:", "Enter a name")
Next
For y = 0 To GradeArray.Length - 1
GradeArray(y) = InputBox("Enter grade number " & y + 1 & " for " & NamesList(0) & " in the box:", "Enter the grades")
Next
For Each item In GradeArray
Sum = Sum + item
Next
Avg = Sum / 5
lstAverages.Text = Avg.ToString
End Sub
Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) Handles btnExit.Click
Me.Close()
End Sub
End Class
I had nothing else better to do, so I took up giving it a try... Also this includes per as you stated: 4 students - 5 grades each, array for students names and a 2D array to hold all their grades. There's a method that passes these to it and performs the averaging of the students grades and then spits them to a listbox as requested ... Happy Coding!
P.S. I didn't do any error handling either, you may want to add that or at least implement something to handle such cases ...
Public Class Form1
Private arrStudents(3) As String 'Student's name array (4)
Private arrScores(3, 4) As Integer 'Students scores (5 each)
'Start getting the data we need
Private Sub btnGetStudents_Click(sender As Object, e As EventArgs) Handles btnGetStudents.Click
Dim strStudent As String = String.Empty
Dim intScore As Integer = 0
Dim intPlace As Integer = 0
'Get the students name...
For i As Integer = 0 To arrStudents.Length - 1
Do Until strStudent <> String.Empty
strStudent = InputBox("Please enter student's name: ", "Gather Student Grades")
Loop
arrStudents(i) = strStudent
'Reset our variable...
strStudent = String.Empty
'Get the students grades...
For s As Integer = 0 To arrScores.Length - 1
Do Until intScore > 0
intScore = CInt(InputBox("Please enter student's scores: ", "Gather Student Scores"))
Loop
If (intPlace = 4 AndAlso i = arrStudents.Length) Then
intPlace = 0
arrScores(i, s) = intScore
intScore = 0
ElseIf intPlace = 4 Then
intPlace = 0
arrScores(i, s) = intScore
intScore = 0
Exit For
Else
arrScores(i, intPlace) = intScore
intPlace += 1
End If
'Reset our variables...
intScore = 0
Next
Next
'Calculate and output the data to the listbox...
GetStudentAverages(arrStudents, arrScores)
End Sub
'Function to average per student grades and then display them all in the listbox...
Private Sub GetStudentAverages(ByVal arStudent As Array, ByVal arScore As Array)
Dim strStudentData As String = String.Empty
Dim intAverage As Integer = 0
Dim intPlace As Integer = 0
'Start averaging the students scores and then add them to the listbox...
For i As Integer = 0 To arStudent.Length - 1
For g As Integer = 0 To arScore.Length - 1
If intPlace = arStudent.Length Then
intAverage += arScore(i, intPlace)
Exit For
Else
intAverage += arScore(i, intPlace)
intPlace += 1
End If
Next
intAverage = CInt(intAverage / 5)
intPlace = 0
'Output the student information...
ListBox1.Items.Add("Student: " & arStudent(i).ToString & " Average: " & intAverage.ToString)
intAverage = 0
Next
End Sub
End Class

Randomizing an array

I wish to implement the Dr.D.E.Knuth's Subtractive RANDOM number generation algorithm. I wish to implement an ATM panel on which when very time user log-in the buttons will be scrambled. every button will change its position.
Here is my code:
Public Sub addbutton()
Dim n As Integer = 0
For i As Integer = 0 To 10
' Initialize one variable
btnArray(i) = New System.Windows.Forms.Button
Next i
While (n < 10)
With (btnArray(n))
.Tag = n + 1 ' Tag of button
.Width = 40 ' Width of button
.Height = 40
FlowLayoutPanel1.Controls.Add(btnArray(n))
.Text = Chr(n + 48)
AddHandler .Click, AddressOf Me.ClickButton
n += 1
End With
End While
End Sub
Then, for sending information to button Text, I used:
Dim btn As Button = sender
TextBox1.Text += btn.Text
Now the major task is to shuffle the btnArray() with Random() function.. But I've failed to do this. I managed to get some code for shuffling the array like follows:
Imports System.Security.Cryptography
Public Class ArrayUtilities
Private Random As RNGCryptoServiceProvider = New RNGCryptoServiceProvider
Private Bytes(4) As Byte
Public Function ShuffleArray(ByVal argArr As Array) As Array
Dim FirstArray As New ArrayList(argArr)
Dim SecoundArray As Array = Array.CreateInstance(GetType(Object), FirstArray.Count)
Dim intIndex As Integer
For i As Integer = 0 To FirstArray.Count - 1
intIndex = RandomNumber(FirstArray.Count)
SecoundArray(i) = FirstArray(intIndex)
FirstArray.RemoveAt(intIndex)
Next
FirstArray = Nothing
Return SecoundArray
End Function
Private Function RandomNumber(ByVal argMax As Integer) As Integer
If argMax <= 0 Then Throw New Exception
Random.GetBytes(Bytes)
Dim intValue As Integer = (BitConverter.ToInt32(Bytes, 0)) Mod argMax
If intValue < 0 Then intValue = -intValue
Return intValue
End Function
End Class
Module Module1
Sub Main()
Dim AU As ArrayUtilities
AU = New ArrayUtilities
Dim GivenArray As Integer() = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
Dim NewArray As Array = AU.ShuffleArray(GivenArray)
Dim i As Integer
Dim stb As New System.Text.StringBuilder
stb.Append("GivenArray = {0,1,2,3,4,5,6,7,8,9}")
stb.Append(vbCrLf)
stb.Append("NewArray = {")
For i = 0 To NewArray.Length - 2
stb.Append(NewArray(i).ToString)
stb.Append(", ")
Next
stb.Append(NewArray(NewArray.Length - 1).ToString)
stb.Append("}")
Console.Write(stb.ToString)
Console.Read()
End Sub
End Module
Yet, when we debug this code we get RANDOMNESS for the array. In the same way, I want randomness for my buttons on form.
Thank you sir. sir i tried the code that u suggested.
I was missing the incremental character "n".
Tthe debug-able code is
flowlayoutpannel.controls.add(out(n)).
but it is not working as i wanted and ABOVE code is just to show the way i want 2 shuffle the BUTTONS.
Is there any simpler way to jst create an button array and randomize them with RANDOM() and adding to form.
My buddies says you are so stupid that u are working on this topic from last 20-25DAYS
There are quite some "shuffle" algorithms (search Fisher-Yates for example) and they are usually not difficult to implement. The EASIEST (imho) way, ist to use LINQ:
Dim r As New Random
Dim out = (From b In btnArray Order By r.Next Select b).ToArray
Maybe the question is not clear: Do you want to shuffle the POSITIONS of the buttons or do you want to shuffle the CONTENT (buttons) of the Array?
To set the position of the buttons randomly in the panel, instead of using n from 0 to 9 in the loop, you can use a random value from 0 to 9. Be sure you don't use the same value twice.
You don't need to use a cryptographically secure random number generator for that, nor do you need a separate class.
Private Shared rng As New Random()
Private Shared Function ShuffleArray(Of T)(arr() As T) As T()
Dim left = Enumerable.Range(0, arr.Length).ToList()
Dim result(arr.Length - 1) As T
For i = 0 To arr.Length - 1
Dim nextIndex = rng.Next(left.Count)
result(i) = arr(left(nextIndex))
left.RemoveAt(nextIndex)
Next
Return result
End Function

Resources