Generating Three Unique Random Numbers in Visual Basic? - vb.net

I have been working on an app lately that displays three random photos. The form consists of three pictureboxes and a button. When the user clicks a button, three different images are shown. The problem is, however, these three images are not always unique, most of the time there will be doubles and often triples too. I tried to implement a function to catch this but all it succeeded at was lowering the chances of identical images. There are over 50 images to choose from, so it's not like there isn't enough. Here is the code for the failed solution I came up with:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
RandomImageOne()
RandomImageTwo()
RandomImageThree()
If imagenumber1.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If
If imagenumber1.Text or imagenumber3.Text = imagenumber2.Text Then
RandomImageTwo()
End If
If imagenumber3.Text or imagenumber2.Text = imagenumber1.Text Then
RandomImageOne()
End If
End Sub
The 'RandomImage' functions generate a random number in a label (eg. imagenumber1), these numbers correlate with the number of one of the 50 images. I realize that this is probably not the smartest method, but I am not familiar with any other way.
I need to be able to either generate three unique numbers, so that I don't have to worry about programming in something to stop double and triple images, or create a solution that works 100% of the time to catch the double or triple images.
Any help would be very much appreciated, especially if it were explained simply. Thank you.

I would generate random image 1 and image 2, testing that image 2 is distinct using a while loop. Only once that that is done would I then move on to generating image three.
Information on the while loop is here.
So in rough code (it's been a while since I used VBA properly):
RandomImageOne()
RandomImageTwo()
do while imagenumber1.text = imagenumber2.text
RandomImageTwo()
loop
RandomImageThree()
do while imagenumber3.text = imagenumber2.text or imagenumber3.text = imagenumber1.text
RandomImageThree()
loop

It might not be the most efficient way but it works...
First create a function that returns a list of three items:
Public Function ProvideUniqueNumbers(NoList As List(Of Integer), _
HowManyToReturn As Integer) As List(Of Integer)
Dim Generator As System.Random = New System.Random()
Dim n As Integer = NoList.Count
Dim index As Integer = Generator.Next(1, n)
Dim ReturnList As List(Of Integer) = New List(Of Integer)
For i = 1 To HowManyToReturn
n = NoList.Count
index = Generator.Next(1, n)
ReturnList.Add(NoList(index))
NoList.RemoveAt(index)
'NoList.Dump()
Next
Return ReturnList
End Function
Then create a list of integers for your collection. For instance:
List(Of Integer) MyList = New List(Of Integer)
For i As Integer = 0 To YourImageArray.Count - 1
MyList.Add(i)
Next
Finally Call the function and distribute the results:
Dim result As List(Of Integer) = ProvideUniqueNumbers(MyList,3)
image1 = YourImageArray(result(0))
image2 = YourImageArray(result(1))
image3 = YourImageArray(result(2))

This isn't a solution but a note.
This doesn't do what you think it does
If imagenumber1.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If
You have to compare each element
If imagenumber1.Text = imagenumber3.Text or imagenumber2.Text = imagenumber3.Text Then
RandomImageThree()
End If

Related

vb.net word cloud indexing

I use a wordcloud based on a list of words and their frequencies. I load the list from a text file and display them in a Listview and image. When the textfile is not indexed (the highest frequencies first) the word cloud doesn't make the words with the highest counts the biggest.
Is there a way to load the words, highest frequencies first, without having to change the list?
Imports WordCloudGen = WordCloud.WordCloud
Imports System.IO
Public Class WordCloud
Private Sub WordCloud_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim lines = File.ReadLines("C:\Users\Gebruiker\Downloads\Words.txt")
Dim Words As New List(Of String) '({100})
Dim Frequencies As New List(Of Integer) '({100})
Dim textValue As String()
Dim items As New List(Of ListViewItem)
For Each line In lines
textValue = line.Split(New Char() {","})
Words.Add(textValue(0))
Frequencies.Add(Integer.Parse(textValue(1)))
items.Add(New ListViewItem(New String() {textValue(0).ToString, textValue(1).ToString}, 0))
Next
ListView1.Items.AddRange(items.ToArray)
Dim wc As WordCloudGen = New WordCloudGen(600, 400)
Dim i As Image = wc.Draw(Words, Frequencies)
ResultPictureBox.Image = i
End Sub
When the textfile is not indexed (the highest frequencies first) the word cloud doesn't make the words with the highest counts the biggest. Is there a way to load the words, highest frequencies first, without having to change the list?
I would recommend a new class to hold your data then you can sort anything you need much easier.
Create a new class: WordsFrequencies
Public Class WordsFrequencies
Public Property Word As String
Public Property Frequency As Integer
End Class
Change your WordCloud_Load routine as below:
Dim WordsFreqList As New List(Of WordsFrequencies)
For Each line As String In File.ReadLines("C:\Users\Gebruiker\Downloads\Words.txt")
Dim splitText As String() = line.Split(","c)
If splitText IsNot Nothing AndAlso splitText.Length = 2 Then
Dim wordFrq As New WordsFrequencies
Dim freq As Integer
wordFrq.Word = splitText(0)
wordFrq.Frequency = If(Integer.TryParse(splitText(1), freq), freq, 0)
WordsFreqList.Add(wordFrq)
End If
Next
If WordsFreqList.Count > 0 Then
' Order the list based on the Frequency
WordsFreqList = WordsFreqList.OrderByDescending(Function(w) w.Frequency).ToList
' Add the sorted items to the listview
WordsFreqList.ForEach(Sub(wf)
ListView1.Items.Add(New ListViewItem(New String() {wf.Word, wf.Frequency.ToString}, 0))
End Sub)
End If
In the above, I would recommend doing a simple For loop with File.ReadLines, this is so you don't have to load the whole file in memory if you're just getting data and parsing it. I'm using the OrderByDescending Method which is part of System.Linq namespace.
As far as this: Dim i As Image = wc.Draw(Words, Frequencies) you then could do something like:
Dim i As Image = wc.Draw(WordsFreqList.Select(Function(wf) wf.Word), WordsFreqList.Select(Function(wf) wf.Frequency))
This will project the Word's into an IEnumerable(String) and then Frequency into an IEnumerable(Integer).

Pasting new records to a Bound DataGridView

Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.

Visual Basic, new to coding. Need to make a list of 100 integers create a shuffled list of 70 results

I'm new to coding and I have got firmly stuck on this.
I've created a list in Visual Basic with
Dim integerStable As New List(Of Integer)()
integerStable.Add(0)
integerStable.Add(1)
integerStable.Add(2)
'through to integerStable.Add(99)
I'm trying to keep this list (it can be shuffled as long as all numbers stay in the list in general) and create a 2nd list where it only has 70 results from that shuffle.
I need that list, so I can call on it to perform some tasks for me later.
Can anyone help me work out how to do this? Remember I'm new to coding, but I'll try to follow along.
One of the most efficient ways to create your list would be as follows:
Dim integerStable As New List(Of Integer)
For i = 1 To 100
integerStable.Add(i)
Next
That at least should save you a lot of typing!!
You could also do the following:
Dim integerStable As New List(Of Integer)
Dim i As Integer
While i <= 100
integerStable.Add(i)
i += 1
End While
**Note though that the latter example will give you 101 items as integer is initially set to 0 **
You also need to remember that the list will be 'indexed' from 0 NOT 1 which is an important thing to remember when it comes to manipulating the items with it.
It can be much simpler.
Private Shared PRNG As New Random
' 100 numbers starting at zero, in random order
Private listOnum As List(Of Integer) = Enumerable.Range(0, 100).OrderBy(Function(x) PRNG.Next).ToList
' list of 70 numbers from list of 100
Private list70 As List(Of Integer) = listOnum.Take(70).ToList
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'did it work?
Dim ct As Integer = 1
For Each n As Integer In list70
Debug.WriteLine("{0}. {1,3}", ct, n)
ct += 1
Next
End Sub
Enumerable.Range takes two arguments. The first is a start number and the second is a count, so in the example it created a list that started at 0 and ended with 99, 100 items. The OrderBy just sorted that list by random numbers.
list70 is created by taking the first 70 items from listOnum.
The Random, PRNG, is created that way so that there is only ONE random that is only initialized once. You can find many problems associated with the incorrect initialization of Random.
edit: Slightly different approach.
Private Shared PRNG As New Random
' 100 numbers starting at zero
Private listOnum As List(Of Integer) = Enumerable.Range(0, 100).ToList
' list of 70 random numbers from list of 100
Private list70 As List(Of Integer) = listOnum.OrderBy(Function(x) PRNG.Next).Take(70).ToList
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'did it work?
Dim ct As Integer = 1
For Each n As Integer In list70
Debug.WriteLine("{0}. {1,3}", ct, n)
ct += 1
Next
'recreate list of 70
list70 = listOnum.OrderBy(Function(x) PRNG.Next).Take(70).ToList
End Sub

How can i create a list of my resources so i can refer to an element using index? - VB.NET

Here's what i have : a simple windows forms application with a PictureBox and a TrackBar.
So here's what i want : i want to be able to put any jpeg or png in the resources and refer to them by the TrackBar1.Value without having to specify their names 1 by 1 in a string array.
The only way i see is to refer somehow to an array containing what's in the Resources.
See, i've came up with this code wich works fine :
Dim list() As String = {"2013-04-03 22.53.41", "2013-04-10 12.43.47",
"2013-05-24 01.44.00", "2013-05-25 11.49.51", "2013-05-25 16.37.10",
"2013-06-06 23.22.46", "2013-07-04 19.59.29", "2013-09-14 12.31.09",
"2013-11-20 20.28.07", "2014-01-03 15.30.21", "2014-01-24 20.12.16",
"2014-03-18 19.03.21", "2014-05-27 20.40.07", "2014-07-21 19.46.37",
"2014-08-05 14.05.09", "2014-09-01 17.41.46", "2014-09-01 22.13.08",
"2014-09-14 17.49.31", "2014-09-15 17.27.55", "2015-05-30 12.45.58"}
' These are 19 iPhone pictures
Private Sub TrackBar1_Scroll(sender As Object, e As EventArgs) Handles TrackBar1.Scroll
picbox.Image = CType(My.Resources.ResourceManager.GetObject(list(TrackBar1.Value)), Image)
End Sub
But that way, i have to manually type the names in a string array...
Thanks a lot.
Something like this should work
Private _resourceNames As String()
Public ReadOnly Property ResourceNames As String()
Get
If _resourceNames Is Nothing Then
Dim thisExe As System.Reflection.Assembly
thisExe = Me.GetType.Assembly
System.Reflection.Assembly.GetExecutingAssembly()
_resourceNames = thisExe.GetManifestResourceNames()
End If
Return _resourceNames
End Get
End Property

How To Read From Text File & Store Data So To Modify At A Later Time

What I am trying to do may be better for use with SQL Server but I have seen many applications in the past that simply work on text files and I am wanting to try to imitate the same behaviour that those applications follow.
I have a list of URL's in a text file. This is simple enough to open and read line by line, but how can I store additional data from the file and query the data?
E.g.
Text File:
http://link1.com/ - 0
http://link2.com/ - 0
http://link3.com/ - 1
http://link4.com/ - 0
http://link5.com/ - 1
Then I will read the data with:
Private Sub ButtonX2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonX2.Click
OpenFileDialog1.Filter = "*txt Text Files|*.txt"
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
Dim AllText As String = My.Computer.FileSystem.ReadAllText(OpenFileDialog1.FileName)
Dim Lines() = Split(AllText, vbCrLf)
Dim list = New List(Of Test)
Dim URLsLoaded As Integer = 0
For i = 0 To UBound(Lines)
If Lines(i) = "" Then Continue For
Dim URLInfo As String() = Split(Lines(i), " - ")
If URLInfo.Count < 6 Then Continue For
list.Add(New Test(URLInfo(0), URLInfo(1)))
URLsLoaded += 1
Next
DataGridViewX1.DataSource = list
LabelX5.Text = URLsLoaded.ToString()
End If
End Sub
So as you can see, above I am prompting the user to open a text file, afterwards it is displayed back to the user in a datagridview.
Now here is my issue, I want to be able to query the data, E.g. Select * From URLs WHERE active='1' (Too used to PHP + MySQL!)
Where the 1 is the corresponding 1 or 0 after the URL in the text file.
In the above example the data is being stored in a simple class as per below:
Public Class Test
Public Sub New(ByVal URL As String, ByVal Active As Integer)
_URL = URL
_Active = Active
End Sub
Private _URL As String
Public Property URL() As String
Get
Return _URL
End Get
Set(ByVal value As String)
_URL = value
End Set
End Property
Private _Active As String
Public Property Active As String
Get
Return _Active
End Get
Set(ByVal value As String)
_Active = value
End Set
End Property
End Class
Am I going completely the wrong way about storing the data after importing from a text file?
I am new to VB.NET and still learning the basics but I find it much easier to learn by playing around before hitting the massive books!
Working example:
Dim myurls As New List(Of Test)
myurls.Add(New Test("http://link1.com/", 1))
myurls.Add(New Test("http://link2.com/", 0))
myurls.Add(New Test("http://link3.com/", 0))
Dim result = From t In myurls Where t.Active = 1
For Each testitem As Test In result
MsgBox(testitem.URL)
Next
By the way, LINQ is magic. You can shorten your loading/parse code to 3 rows of code:
Dim Lines() = IO.File.ReadAllLines("myfile.txt")
Dim myurls As List(Of Test) = (From t In lines Select New Test(Split(t, " - ")(0), Split(t, " - ")(1))).ToList
DataGridViewX1.DataSource = myurls
The first line reads all lines in the file to an array of strings.
The second line splits each line in the array, and creates a test-item and then converts all those result items to an list ( of Test).
Of course this could be misused to sillyness by making it to a one-row:er:
DataGridViewX1.DataSource = (From t In IO.File.ReadAllLines("myfile.txt") Select New Test(Split(t, " - ")(0), Split(t, " - ")(1))).ToList
Wich would render your load function to contain only following 4 rows:
If OpenFileDialog1.ShowDialog() = DialogResult.OK Then
DataGridViewX1.DataSource = (From t In IO.File.ReadAllLines("myfile.txt") Select New Test(Split(t, " - ")(0), Split(t, " - ")(1))).ToList
LabelX5.Text = ctype(datagridviewx1.datasource,List(Of Test)).Count
End If
You can query your class using LINQ, as long as it is in an appropriate collection type, like List(of Test) . I am not familiar completely with the VB syntax for LINQ but it would be something like below.
list.Where(Function(x) x.Active == "1").Select(Function(x) x.Url)
However, this isnt actually storing anything into a database, which i think your question might be asking?
I think you are reinventing the wheel, which is not generally a good thing. If you want SQL like functionality just store the data in a SQL DB and query it.
There are a lot of reasons you should just use an existing DB:
Your code will be less tested and thus more likely to have bugs.
Your code will be less optimized and probably perform worse. (You were planning on implementing a query optimizer and indexing engine for performance, right?)
Your code won't have as many features (locking, constraints, triggers, backup/recovery, a query language, etc.)
There are lots of free RDBMS options out there so it might even be cheaper to use an existing system than spending your time writing an inferior one.
That said, if this is just an academic exercise, go for it. However, I wouldn't do this for a real-world system.