Sorting using an index number - vb.net

How do i implement a index based sort in vb.net
Could someone guide me. By giving me a head start
eg
array = 9 8 7 6 5 4 3 2 1
Index Number = 5
Counting from 9 and the 5th character is 5.
So the first sorted character is 5.
Remove 5 and remains 9 8 7 6 4 3 2 1
start value should be from 4(current 5th character) now 5th character is 9 since it's circular
unsorted array : 8 7 6 4 3 2 1
sorted array : 5 9
any hints

First off, I definitely wouldn't call this "sorting"; it's a bit more like deterministically (non-randomly) "shuffling" the array. But I'll take a stab at this…
Basically, start by using List(Of T) rather than arrays, because they allow you to easily and remove at any point in the list, rather than arrays which are a fixed size. Next, use a running counter to track your current position in the input list, and use the Mod operator to make the index effectively 'wrap' around the end of the list. Use a While loop to continue until all the items from the input have been processed. It should look something like this:
Dim input As New List(Of Integer) From { 9, 8, 7, 6, 5, 4, 3, 2, 1 }
Dim stepBy As Integer = 5
Dim index As Integer = 0
Dim output As New List(Of Integer)
While input.Count > 0
index = (index + stepBy - 1) Mod input.Count
output.Add(input(index))
input.RemoveAt(index)
End While
In this case the output is:
5, 9, 3, 6, 7, 4, 1, 8, 2

I thought this was an interesting question and I loved the answer by p.s.w.g....
...but I was curious to see if any of the built-in .Net collections would facilitate a fairly trivial circular list.
This is what I came up with using the LinkedList class:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim originalIntegers() As Integer = {9, 8, 7, 6, 5, 4, 3, 2, 1}
Dim output As New List(Of Integer)
Dim circleInts As New CircularIntegerList(originalIntegers)
circleInts.MoveForward(4) ' move forward 4 from the 1st to get the 5th slot
While circleInts.Count > 0
If circleInts.Current.HasValue Then
output.Add(circleInts.Current)
End If
circleInts.RemoveCurrent() ' next number in sequence becomes the current one
circleInts.MoveForward(4) ' move forward another 4, net jump is 5 from the deleted one
End While
Dim result As String = String.Join(",", output.Select(Function(x) x.ToString).ToArray)
Debug.Print(result)
End Sub
End Class
Public Class CircularIntegerList
Private values As New LinkedList(Of Integer)
Private _current As LinkedListNode(Of Integer)
Public Sub New(ByVal data() As Integer)
If data.Length > 0 Then
values = New LinkedList(Of Integer)(data)
_current = values.First
End If
End Sub
Public ReadOnly Property Current As Integer?
Get
If Not IsNothing(_current) Then
Return _current.Value
Else
Return Nothing
End If
End Get
End Property
Public ReadOnly Property Count As Integer
Get
Return values.Count
End Get
End Property
Public Sub RemoveCurrent()
If Not IsNothing(_current) Then
Dim tmp As LinkedListNode(Of Integer) = If(IsNothing(_current.Next), values.First, _current.Next)
values.Remove(_current)
If values.Count > 0 Then
_current = tmp
Else
_current = Nothing
End If
End If
End Sub
Public Sub MoveForward(Optional ByVal NumberOfJumps As Integer = 1)
If Not IsNothing(_current) Then
For i As Integer = 1 To NumberOfJumps
_current = If(IsNothing(_current.Next), values.First, _current.Next)
Next
End If
End Sub
End Class

Related

Fill in missing numbers in different lists twice. ArgumentOutOfRangeException

I need your help to prepare data. I am reading a byte array. I make bytes to unsigned integers. I read in different blocks of that array and write the UInt32s in 5 lists in total. The data has been stored compressed; that is, some spaces are missing and I need to fill them up. To make it clear, I made a compilable test project for you and wrote the data into an excel file.
This is the original data. From the left to the right: Sizes, Addresses, Indexes, Number_of_items, Description
You can see that in column C the 2, 3, and 4 are missing. So I select columns C through E, and move them down 3 rows. I fill the gaps with 2, 3, 4 in column C and 1, 1, 1 in the other two columns.
I do this until I reach the end of column B. Columns B, C, D, and E must have the same length.
Where I have a little problem
I fail because a While or For loop evaluates the List.Count property only once. That is, if I add something to a list within the loop, the loop doesn't run often enough. I've provisionally worked around this by writing While True and catching an OutOfRangeException. Maybe someone has a better idea; or even an idea that completely replaces my approach :D
Step № 2
If a row has a 2 in column D, I select columns B through E below the 2, and move the contents down one row (only one, because the difference is 1).
I want to do this until I get to the bottom of the table. This will make all columns the same length.
Again, I have the problem that I use While True and go out using an exception. Does anyone have a better idea?
FormMain.vb
Public NotInheritable Class FormMain
Private Sizes As New List(Of UInt32) From {
58_355UI,
20_270UI,
4_830UI,
4_443UI,
25_177UI,
8_844UI,
4_101UI,
4_200UI,
14_991UI,
12_639UI,
12_894UI,
14_165UI,
12_954UI,
26_670UI,
7_388UI}
Private Addresses As New List(Of UInt32) From {4_323UI, 62_706UI, 83_646UI, 88_935UI, 93_883UI, 128_259UI, 132_718UI,
137_254UI, 152_590UI, 178_485UI, 193_022UI, 206_718UI}
Private Indexes As New List(Of UInt32) From {1UI, 5UI, 6UI, 9UI, 10UI, 12UI}
Private NumberOfItems As New List(Of UInt32) From {1UI, 2UI, 1UI, 2UI, 1UI, 2UI}
Private Description As New List(Of UInt32) From {1UI, 1UI, 1UI, 1UI, 1UI, 1UI}
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Dim RopD As New Reprocessing_of_parsed_data(Sizes, Addresses, Indexes, NumberOfItems, Description)
RopD.Fill_gaps()
End Sub
End Class
Reprocessing_of_parsed_data.vb
Public NotInheritable Class Reprocessing_of_parsed_data
Public Property Sizes As New List(Of UInteger)
Public Property Addresses As New List(Of UInteger)
Public Property Indexes As New List(Of UInteger)
Public Property Number_of_items As New List(Of UInteger)
Public Property Description As New List(Of UInteger)
Public Sub New(sizes As List(Of UInt32), addresses As List(Of UInt32), indexes As List(Of UInt32), number_of_items As List(Of UInt32), description As List(Of UInt32))
Me.Sizes = sizes
Me.Addresses = addresses
Me.Indexes = indexes
Me.Number_of_items = number_of_items
Me.Description = description
End Sub
Public Sub Fill_gaps()
Dim counterForAddressesList As Integer = 0
'Dim ListCount As Integer = Indexes.Count - 2
Dim i As Integer = 0
While True 'i < ListCount - 2
Try
Dim delta As Integer = CInt(Indexes(i + 1) - Indexes(i)) - 1
Dim number As UInt32 = Indexes(i)
While delta > 0
number += 1UI
counterForAddressesList += 1
Indexes.Insert(CInt(number) - 1, number)
Number_of_items.Insert(CInt(number) - 1, 1UI)
Description.Insert(CInt(number) - 1, 1UI)
delta -= 1
'ListCount += 1
End While
counterForAddressesList += 1
i += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
' Step 2
Dim j As Integer = 0
While True
Try
If Number_of_items(j) > 1UI Then
Dim delta As Integer = CInt(Number_of_items(j)) - 1
While delta > 0
Addresses.Insert(j + 1, UInteger.MaxValue)
Indexes.Insert(j + 1, UInteger.MaxValue)
Number_of_items.Insert(j + 1, UInteger.MaxValue)
Description.Insert(j + 1, UInteger.MaxValue)
delta -= 1
j += 1
End While
End If
j += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
End Sub
End Class
It is never a good idea to catch an index out of bounds exception in a Try-Catch-statement. Only conditions you are not in control of (often I/O errors) should be handled at runtime. An index being out of bounds is a design error and must be fixed at design time.
I extracted the two steps from Sub Fill_gaps into two new methods to make the code easier to read and test.
Public Sub Fill_gaps() ' A better name would be "Decompress"
PrintTable() 'For testing
FillGaps()
PrintTable() 'For testing
AddMissingNumberOfItems()
PrintTable() 'For testing
End Sub
I also added a method PrintTable for testing
Private Sub PrintTable()
Console.WriteLine()
Console.WriteLine($" A B C D E")
For i = 0 To Sizes.Count - 1
Dim A = Sizes(i)
Dim B = If(i < Addresses.Count, Addresses(i), 0UI)
Dim C = If(i < Indexes.Count, Indexes(i), 0UI)
Dim D = If(i < NumberOfItems.Count, NumberOfItems(i), 0UI)
Dim E = If(i < Description.Count, Description(i), 0UI)
Console.WriteLine($"{A,10}{B,10}{C,10}{D,10}{E,10}")
Next
End Sub
Step 1: fill the gaps (the method is self-explanatory):
Private Sub FillGaps()
' Fill gaps in columns C, D and E.
' The number of Addresses B indicates the total number of indexes.
' Append empty items to C, D and E until the list counts matches the
' expected total number of indexes.
Dim originalIndexCount = Indexes.Count 'Save original count
Do While Indexes.Count < Addresses.Count
Indexes.Add(CUInt(Indexes.Count + 1)) ' Make index 1-based
NumberOfItems.Add(1)
Description.Add(1)
Loop
'Move the rows to where the index indicates.
'We do it backwards to not overwrite existing items.
For i As Integer = originalIndexCount - 1 To 0 Step -1
Dim targetIndex = CInt(Indexes(i)) - 1 ' Subtract 1, indexes are 0-based
If targetIndex <> i Then
' Copy to target position
Indexes(targetIndex) = Indexes(i)
NumberOfItems(targetIndex) = NumberOfItems(i)
Description(targetIndex) = Description(i)
'Clear resp. initialize old row
Indexes(i) = CUInt(i + 1) ' Make index 1-based
NumberOfItems(i) = 1
Description(i) = 1
End If
Next
End Sub
Step 2:
Private Sub AddMissingNumberOfItems()
' Insert empty rows after items with NumberOfItems > 1.
' We do it backwards to not mess up our indexes.
For i As Integer = Indexes.Count - 1 To 0 Step -1
For k As UInteger = 2 To NumberOfItems(i)
Addresses.Insert(i + 1, 0)
Indexes.Insert(i + 1, 0)
NumberOfItems.Insert(i + 1, 0)
Description.Insert(i + 1, 0)
Next
Next
End Sub
If you use the following test list for the descriptions, you will better see which rows have been moved or added
Private Description As New List(Of UInt32) From {2UI, 3UI, 4UI, 5UI, 6UI, 7UI}

Showing Multiple Numbers from RNG

What I am trying to do is create an RNG button that will generate 5 unique numbers between 1-99. This is what I have so far:
Private Sub GenerateButton_Click(sender As Object, e As EventArgs) Handles GenerateButton.Click
Dim rand As New Random()
Dim rememberset As New HashSet(Of Integer)
Dim value As Integer
While rememberset.Count < 6
value = rand.Next(1, 99)
If rememberset.Add(value) Then
LotteryLabel.Text = value.ToString()
End If
End While
End Sub
Every time I click the generate button, I just get 1 number. I'm trying to get 5 to display in this format (1, 2, 3, 4, 5). Any ideas? Thanks.
Your LotteryLabel.Text is always set to the string of only one number ( I guess you will see only the last number)
You need to append the value: Easy (but maybe slow) solution should be
LotteryLabel.Text = LotteryLabel.Text & ", " & value.ToString()

Declaring a one dimensional array of 6 and showing a random value between 1-6 in a textbox

I need help with my program. I declared a one-dimensional array of 6 and I want to show random values between 1-6 in a text box
My question is how do I show values in my array in textbox1.text?
Here is my code:
Public Sub ClickMyFirstClassButton()
If FirstClass.Checked = True Then
'This piece of code declares an array
Dim Seats As Integer()
'This is a One Dimensional Array
ReDim Seats(6)
TextBox1.Text = (String.Format("First Class is checked. The number of seats are : ", (Seats)))
'ElseIf FirstClass.AutoCheck = True Then
'MessageBox.Show("FirstClass is Auto checked")
End If
End Sub
I messed around with my program and this is what I did.
Public Sub ClickMyFirstClassButton()
If FirstClass.Checked = True Then
'Dim Seats As Integer() = {1, 2, 3, 4, 5, 6}
Dim Seats(0 To 6) As Integer
Seats(0) = 1
Seats(1) = 2
Seats(2) = 3
Seats(3) = 4
Seats(4) = 5
Seats(5) = 6
TextBox1.Text = (String.Format("First Class is checked. Your seat is : {0}", Seats(RandomNumber(Seats))))
MessageBox.Show(String.Format("First Class is checked. Your seat is : {0}", Seats(RandomNumber(Seats))))
'ElseIf FirstClass.AutoCheck Then
'MessageBox.Show("FirstClass is Auto checked")
End If
End Sub
Where you currently have "(Seats)", replace it with string.join(", ", Seats) and see if you like those results.
(Though I admit I'm not sure what this has to do with random values, it should display the values in your array.)
I am still unsure as to why you are using an array in this way If you just want a number between 1 and 6, this will do what you want:
Public Sub ClickMyFirstClassButton()
If FirstClass.Checked = True Then
TextBox1.Text = (String.Format("First Class is checked. The number of seats are : " & CInt(Math.Ceiling(Rnd() * 6)) + 1))
End If
End Sub
Is this a learning exercise? If so I would be keen to see the question that has been asked of you as it doesn't make much sense to me currently.

VB.net Error when storing lists in array

I have the following code, the objective of it is to take an initial list and to take each element within the list and store it in an array of lists, with each list in the array, holding each element in its own list. For instance
The list 2, 2, 3, 3, 3, 3, 5, 5, 7, 9, 9. Would create five lists:
List 1: 2, 2
List 2: 3, 3, 3, 3,
List 3: 5, 5
list 4: 7
List 5: 9, 9
This is my current code:-
Dim cnt As Integer = 0
Dim lists(uniqueFactors.Count) As List(Of Integer)
Dim saver As Integer = factors.Item(0)
Console.WriteLine(saver)
For Each i In factors
lists(cnt).Add(i)
If saver <> i Then
cnt = cnt + 1
End If
saver = i
Next
Thanks all in advance!
You'd probably be better off using a Dictonary<TKey, TValue>.
Dim storage As New Dictionary(Of Integer, Integer)
' Number Occurrences
' | |
storage.Add(2, 2)
storage.Add(3, 4)
storage.Add(5, 2)
storage.Add(7, 1)
storage.Add(9, 2)
You can iterate the list like this:
For Each item As KeyValuePair(Of Integer, Integer) In storage
Dim number As Integer = item.Key
Dim occurrences As Integer = item.Value
Next
Get the number of occurrences for a given number like this:
Dim number As Integer = 9
Dim occurrences As Integer = storage(number) 'Return: 2
Change the number of occurrences:
storage.Item(number) = 4 'Set to 4
storage.Item(number) += 1 'Increase from 2 to 3
storage.Item(number) -= 1 'Decrease from 2 to 1
Create an enumerator, array and/or list of occurrences for a given number:
Dim iterator As IEnumerable(Of Integer) = Enumerable.Repeat(number, occurrences).ToList()
Dim array As Integer() = Enumerable.Repeat(number, occurrences).ToArray()
Dim list As List(Of Integer) = Enumerable.Repeat(number, occurrences).ToList()
You can also write an extension method:
Public Module Extensions
<System.Runtime.CompilerServices.Extension()>
Public Function ToList(Of TKey)(source As Dictionary(Of TKey, Integer), key As TKey) As List(Of TKey)
Return Enumerable.Repeat(key, source.Item(key)).ToList()
End Function
End Module
Now you create a list by simply writing:
Dim occurrences As List(Of Integer) = storage.ToList(number)
Why not use a List of Lists like this?
Dim last As Integer
Dim first As Boolean = True
Dim ints As List(Of Integer)
Dim lists As New List(Of List(Of Integer))
For Each i In factors
If first Then
first = False
ints = New List(Of Integer)
ints.Add(i)
lists.Add(ints)
last = i
ElseIf i = last Then
ints.Add(i)
Else
ints = New List(Of Integer)
ints.Add(i)
lists.Add(ints)
last = i
End If
Next

Why my code in VB is not working?

So the task is:
Write a program that contains a text box, a list box and a command button. Put a label above the text box that tells the user to type a number from 1 to 10 inside the text box. When the user clicks the command button, check the text box for a valid number and issue an error message box if the number isn't inside the expected range or in case of empty input. If the user entered a valid number, assign it to the integer variable n and use a For...Next loop to accumulate the sum of the first n elements of an array declared with the statement Dim numList() as Integer = {2, 4, 10, 5, 6, 8, 9, 3, 2, 4} Display the sum in the list box.
Private Sub bCom_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bCom.Click
Dim n As Integer = CInt(tNum.Text)
If CDbl(tNum.Text) > 10 Then
MessageBox.Show("Please Enter a Number from 1 to 10")
tNum.Focus()
End If
Dim numList() As Integer = {2, 4, 10, 5, 6, 8, 9, 3, 2, 4}
Dim sum As Integer
For Each n As Integer In numList
sum += n
Next
lOut.Items.Add(sum)
End Sub
End Class
You are using a for each loop instead of a for loop, so you iterate over each element in the array, not just the first n ones.
You should use something like:
For x = 0 To n - 1
sum += numList(x)
Next
Some more problems:
No need to convert the text to double if you already converted it to an integer the line before.
You should use Int32.TryParse in case the user enters something that is not a number.
You just check if the number is greater 10, but not if it is smaller than 1.
If the number is invalid, you display a message to the user, then you just keep going on,
but you should exit your method
As others have stated, you need an indexed For..Loop, not a ForEach loop.
You might also consider moving the "is it a valid number" logic to a separate function.
Public Class Form1
Private Sub btnCompute_Click(sender As System.Object, e As System.EventArgs) Handles btnCompute.Click
'is the number valid?
Dim n As Integer = -1
If IsInputValid(txbInput.Text, n) Then
Dim sum As Integer = 0
'declare an array of numbers
Dim numList() As Integer = {2, 4, 10, 5, 6, 8, 9, 3, 2, 4}
'accumulate the sum of the first N elements of the array
For i As Integer = 0 To n - 1
sum += numList(i)
Next
'refresh the listbox with the sum
lbxOutput.Items.Clear()
lbxOutput.Items.Add(sum.ToString)
Else
MessageBox.Show("Please enter a number between 1 and 10")
End If
End Sub
Private Function IsInputValid(input As String, ByRef validnumber As Integer) As Boolean
'input must be a number between 1 and 10
'if it is valid, assign it to the byref validNumber parameter.
Dim i As Integer = -1
If Integer.TryParse(input, i) Then
If i >= 1 AndAlso i <= 10 Then
validnumber = i
Return True
End If
End If
validnumber = i
Return False
End Function
End Class
If I understand your question correctly, you want to sum the first n digits of the array numList.
If this is the case then you need to change your loop code in this way
For x = 0 To n -1
sum += numList(x)
Next
Use x as the indexer to the numList array, start at the first element of the array (x=0) and stop when the indexer has reached the value of n - 1
With this you could satisfy the requirements exactly how they have been asked.
If you wish you could also show an elegant approach using LinQ without an explict loop
sum = numList.Take(n).Sum()
However, before going fancy, it is better that you remove some obvious errors in your code
if Not Int32.TryParse(tNum.Text, n) Then
MessageBox.Show("Error......")
tNum.Focus()
Return
End If
if n < 1 OrElse n > 10 Then
MessageBox.Show("Error......")
tNum.Focus()
Return
End if
And do not use the old fashioned function like CInt e CDbl that are kept only for compatibility with VB6. Use the newest methods like Int32.TryParse, Convert.ToInt32 etc...