How to find the largest integer(s) between 3 integers - vb.net

I would like to find the largest integer(s) between 3 integers.
I could do this by nesting If statements. Since I have further code to write however this would be long and untidy.
I was wondering if there was an easier way to find the largest integer(s) (including if let's say A and B are equal but both higher than C).
P.S Can you do this with 2-D arrays?

Use LINQ to do this:
Dim numbers() As Integer = {1, 3, 5}
Dim max As Integer = numbers.Max()
Debug.Write("Max number in numbers() is " & max.ToString())
Output:
Edited as per conversation with OP on wanting to know which genre was ranked the best.
When asked How do you get the data? OP responds with:
I have a text file containing movie|genre on every line. I read this and count which genre (out of 3) is the highest.
I have drafted up some code which reads from a text file and populates a class.
First let me show you the code:
Dim myFilms As New Films
Using sr As New IO.StreamReader("C:\films.txt")
Do Until sr.Peek = -1
Dim columns As String() = sr.ReadLine().Split(New Char() {"|"c}, StringSplitOptions.RemoveEmptyEntries)
'columns(0) = film name
'columns(1) = genre
myFilms.Add(New Film(columns(0), columns(1)))
Loop
End Using
If myFilms.Count > 0 Then
Dim bestGenre = myFilms.GetBestGenre()
'Go off and read the genre file based on bestGenre
End If
From the above code you can see the class Films being populated with a new Film. I then call a method from the Films class, but only if there are films to choose from. Let me show you the class structure for both these:
Film:
Public Class Film
Public Key As String
Public Sub New(ByVal filmName As String,
ByVal genre As String)
_filmName = filmName
_genre = genre
End Sub
Private _filmName As String
Public ReadOnly Property FilmName As String
Get
Return _filmName
End Get
End Property
Private _genre As String
Public ReadOnly Property Genre As String
Get
Return _genre
End Get
End Property
End Class
Films:
Public Class Films
Inherits KeyedCollection(Of String, Film)
Protected Overrides Function GetKeyForItem(ByVal item As Film) As String
Return item.Key
End Function
Public Function GetBestGenre() As String
Return Me.GroupBy(Function(r) r.Genre).OrderByDescending(Function(g) g.Count()).First().Key
End Function
End Class
I must note that although this code does work it may come unstuck if you have 2 or more genres which are joint top. The code still works however it only returns one of the genres. You may want to expand on the code to suit your needs based on that scenario.

Try something like this:
Dim max As Integer
max = integer1
If integer2 > max Then
max = integer2
End If
If integer3 > max Then
max = integer3
End If
Not many more ways that I can think of off the top of my head to do this.

Something along these lines will work for any number of integers.
Put the numbers into an array then use a For[...]Next statement to loop through the array comparing the current member with max. If max is lower, set it to the current member. When the loop terminates, max will contain the highest number:
Dim nums() As Integer = {1, 2, 3}
Dim max As Integer
For i = 0 To nums.Length - 1
If max < nums(i) Then
max = nums(i)
End If
Next

Related

Unable to cast object error when attempting to return key/value pairs by querying a datacontext

I am attempting to get data from a datacontext. Normally, I've had no problems doing it, but I'm having trouble trying to return a list of key/value pairs.
Basically I'm attempting to grab all unique names from a table as the key column and the number of times they appear in the table as the value column.
My data would look like this:
apple 5
banana 1
dragonfruit 3
.
.
.
The full error message is:
Unable to cast object of type 'System.Data.Linq.DataQuery1[VB$AnonymousType_32[System.String,System.Int32]]' to type 'System.Collections.Generic.List`1
The code I'm using is this:
Dim indicators As List(Of Object)
Public Sub GetIndicatorData()
Using context = new A_DataContext
indicators = (From p In chartdata Group p By __groupByKey1__ = p.INDK8R Into g = Group
Select New With {.name = __groupByKey1__, .count = g.Count()}).AsEnumerable()
indDataSource = indicators
End Sub
but I've also tried to:
Return indicators as a list
Return indicators as an enumerable.
Use a class to encapsulate your anonymous type
Public Class NameAndCount
Public ReadOnly Property Name As String
Public ReadOnly Property Count as Integer
Public Sub New(name As String, count As Integer)
Me.Name = name
Me.Count = count
End Sub
End Class
' ...
Private indicators As IEnumerable(Of NameAndCount)
Public Sub GetIndicatorData()
Using context = new A_DataContext
indicators = From p In chartdata Group p By __groupByKey1__ = p.INDK8R Into g = Group
Select New NameAndCount(__groupByKey1__, g.Count())
indDataSource = indicators.ToList()
End Using
End Sub
Figured it out. I changed the declaration of indicators to simply be an object then removed the .enumerable (or .toList) from the query result.
Thank you for the consideration and time.

Binary Searching in a List and Referring to a Specific Column of the List

I have some two column data that I read from a file and put into a list then sort alphabetically.
//The file
Hummus,0.75
Chili,0.50
Tabouli,1.25
Tzatziki,0.50
//Declaring the variables and public properties
Dim extraList As List(Of extra)
Public Class extra
Implements IComparable(Of extra)
Public Property Name As String
Public Property Price As Decimal
'Public Property extraList As List(Of extra)
Public Function CompareTo(other As extra) As Integer Implements IComparable(Of extra).CompareTo
Return Me.Name.CompareTo(other.Name)
End Function
End Class
//Puts the data into a list and sorts it
Sub Get_Extras_List()
'Reads the extras file and puts the information into a list, splitting the name of the extra and the price into separate columns
Dim allExtras = From line In System.IO.File.ReadLines("C:\Users\ExtrasList.txt")
Let Columns = line.Split(","c)
Where Columns.Length = 2
Let Price = Decimal.Parse(Columns(1).Trim())
Let Name = Columns(0).Trim()
Select New extra With {.Name = Name, .Price = Price}
extraList = allExtras.ToList()
'Sort the list alphabetically
extraList.Sort()
End Sub
Now I need to code a method that allows the user to type in an extra and search for it using a binary search to see if it exists. So far I have tried this but it just doesn't work and even if it did how do I get it to return a true or false value? (If it exists or not?)
Sub Search_Extras_List()
Dim strSearchExtra As String = Console.ReadLine()
Console.WriteLine(vbLf & "BinarySearch for '{0}':", strSearchExtra)
Dim index As Integer =
List(Of extra).BinarySearch(extraList.Name, strSearchExtra)
End Sub
Finally I have to get the user to choose one of the extras and then add the price of it to the total price. How do I refer to the price? extraList.Price? extra.Price? etc.
If you want to do a binary search like that, you will need to write a comparer.
Referring to List(Of T).BinarySearch Method (T, IComparer(Of T)), it could be
Public Class ExtraComparer
Implements IComparer(Of extra)
Public Function Compare(ByVal x As extra, ByVal y As extra) As Integer Implements IComparer(Of extra).Compare
If x Is Nothing Then
If y Is Nothing Then
' If x is Nothing and y is Nothing, they're equal.
Return 0
Else
' If x is Nothing and y is not Nothing, y is greater.
Return -1
End If
Else
' If x is not Nothing...
If y Is Nothing Then
' ...and y is Nothing, x is greater.
Return 1
Else
' ...and y is not Nothing, compare the names of the two extras.
'
Return x.Name.CompareTo(y.Name)
End If
End If
End Function
End Class
Which you can test with
' Get some item from the data so we know it is present...
Dim a = extraList(2).Name
Dim lookFor As New extra With {.Name = a}
Dim idx = extraList.BinarySearch(lookFor, New ExtraComparer)
Console.WriteLine($"Index of {a} is {idx}.")
(although you would probably want to do a case-insensitive string comparison).
If the index returned is negative then the item was not found. (See the documentation linked to above for more details.)
However, you might find it easier to use LINQ:
Dim a = extraList(2).Name
Dim chosenExtra = extraList.FirstOrDefault(Function(x) x.Name.Equals(a, StringComparison.CurrentCultureIgnoreCase))
If chosenExtra IsNot Nothing Then
Console.WriteLine($"User chose {chosenExtra.Name} at a price of {chosenExtra.Price}")
Else
Console.WriteLine($"User's choice of ""{a}"" was not found.")
End If
I used a case-insensitive comparison in there.
Or Just present the user with a dropdown of the available extras so that they don't have to type it.

Generate a property name in a loop

I'm trying to find a way of loading a single record with 25 columns into a datatable.
I could list all 25 variables called SPOT1 to SPOT25 (columns in the datatable) but I'm looking for more concise method like using a loop or dictionary.
The code below shows two methods, a 'long' method which is cumbersome, and a 'concise' method which I'm trying to get help with.
Public dctMC As Dictionary(Of String, VariantType)
Dim newMC As New MONTE_CARLO()
'long method: this will work but is cumbersome
newMC.SPOT1=999
newMC.SPOT2=887
...
newMC.SPOT25=5
'concise method: can it be done more concisely, like in a loop for example?
Dim k As String
For x = 1 To 25
k = "SPOT" & CStr(x)
newMC.K = dctMC(k) 'convert newMC.k to newMC.SPOT1 etc
Next
'load record
DATA.MONTE_CARLOs.InsertOnSubmit(newMC)
Per the others, I think there are better solutions, but it is possible...
Public Class MONTE_CARLO
Private mintSpot(-1) As Integer
Property Spot(index As Integer) As Integer
Get
If index > mintSpot.GetUpperBound(0) Then
ReDim Preserve mintSpot(index)
End If
Return mintSpot(index)
End Get
Set(value As Integer)
If index > mintSpot.GetUpperBound(0) Then
ReDim Preserve mintSpot(index)
End If
mintSpot(index) = value
End Set
End Property
End Class
Usage...
Dim newMC As New MONTE_CARLO
For i As Integer = 0 To 100
newMC.Spot(i) = i
Next i
MsgBox(newMC.Spot(20))

More effective loop

I'm making a player-match-up program in Visual Basic. The program is supposed to pick random registered players and and pair them. I'm currently working on the odd-number-of-players-part.
The solution I have is working but is perhaps not that effective. Is there a better way for me to write this code?
The code is supposed to pick the random players and make sure they are not picked again. As you see, for the code to work i must make it loop thousands of times. If I don't some of the players won't show up in the listbox. Is there a better solution???
In case it's confusing "spiller" is norwegian for "player"
For i As Integer = 0 To 100000
Dim spiller1 As Integer
Dim spiller2 As Integer
Do
spiller1 = CInt(Math.Floor(Rnd() * spillerListe.Count))
spiller2 = CInt(Math.Floor(Rnd() * spillerListe.Count))
Loop Until CBool(spiller1 <> spiller2)
If brukteSpillere(spiller1) = False And brukteSpillere(spiller2) = False Then
brukteSpillere(spiller1) = True
brukteSpillere(spiller2) = True
lstSpillere.Items.Add(spillerListe(spiller1).ToString + " VS. " + spillerListe(spiller2).ToString())
End If
Next i
This is a mess... Have a List(Of Integer) with all the available index.
Loop while availableIndex.Count > 1
Pick a random index from availableIndex and remove it from that list
Pick a random index from availableIndex and remove it from that list
Add these two index to the list of pairs
End Loop
that way you don't need to check if the random values are the same or if they were already picked.
Now, if you don't want to create a list. Then threat the random number not as an index, but as the number of items to check.
Delta = RandomNumber
x = 0
For i As Integer = 0 To itemList.Count-1
If Not itemList(i).IsChoosen Then
x += 1
If x = Delta Then
' i is now your item to pick
itemList(i).IsChoosen = True
Exit For
End If
End If
Next
There are two efficient ways in approaching this problem:
Sort your player list by random number, then match up 1 with 2, 3 with 4 and so on.
Dim r As New Random
Dim randomListe = spillerListe.OrderBy(Function() r.Next).ToList
Generate two random numbers from your range, match up those players into a separate List, remove players from the original list. General two more random numbers from a smaller range (original minus 2), match up, etc.
EDIT: Having looked at MSDN, List has O(n) performance for RemoveAt, so it's not quite efficient, better be using a dictionary, which is O(1) at removing items, so instead of spillerListe have some spillerDicte, where you would add entries in a form (key = index, value = item).
Instead of working with integers, what if you keep your players name in a list and, after picking a player you remove it from the list. Probably this will not be the best performant solution, but it is clear what are you trying to do
Dim lstSpillere = new List(Of String)() ' Just for the example below
Dim spillerListe = new List(Of String)() from {"Marc", "John", "Steve", "George", "David", "Jeremy", "Andrew" }
Dim rnd = new Random()
While spillerListe.Count > 1
Dim firstPlayer = spillerListe(rnd.Next(0, spillerListe.Count))
spillerListe.Remove(firstPlayer)
Dim secondPlayer = spillerListe(rnd.Next(0, spillerListe.Count))
spillerListe.Remove(secondPlayer)
lstSpillere.Add(firstPlayer + " VS. " + secondPlayer)
' for debug purpose....
Console.WriteLine(firstPlayer & " VS. " & secondPlayer)
End While
if spillerListe.Count > 0 Then
Console.WriteLine("Excluded from play list is:" & spillerListe(0))
End if
The important key here is the generation of Random instance that should be outside the loop to avoid to generate the same number in the short time period required by the loop to execute.
Try this:
Module Module1
Dim rnd As New Random
Sub Main()
Dim RegisteredPlayers As New List(Of Player)
' Fill List (example 100 players)
For index As Integer = 1 To 100
RegisteredPlayers.Add(New Player(String.Format("Player{0}", index)))
Next
'Sort Players using a random number
Dim SortedPlayersArray = RandomSortItems(RegisteredPlayers.ToArray())
'Pair players by selecting 2 consequative ones from randomly sorted array
Dim Matches As New List(Of Match)
For index As Integer = 1 To SortedPlayersArray.Length Step 2
Dim m As Match = New Match(SortedPlayersArray(index - 1), SortedPlayersArray(index))
Matches.Add(m)
Debug.WriteLine(m.ToString())
Next
' Match Player48 vs. Player79
' Match Player3 vs. Player53
' Match Player18 vs. Player43
' Match Player85 vs. Player1
' Match Player47 vs. Player56
' Match Player23 vs. Player66
' etc..
End Sub
Public Function RandomSortItems(Of T)(ByVal items As T()) As T()
Dim sorted As T() = New T(items.Length-1) {}
Array.Copy(items, sorted, sorted.Length)
Dim keys As Double() = New Double(items.Length-1) {}
For i As Integer = 1 To items.Length
keys(i - 1) = rnd.NextDouble()
Next
Array.Sort(keys, sorted)
Return sorted
End Function
End Module1
Public Class Player
Dim m_name As String
Public Sub New(ByVal player_name As String)
m_name = player_name
End Sub
Public ReadOnly Property Name() As String
Get
Return m_name
End Get
End Property
Public Overrides Function ToString() As String
Return m_name
End Function
End Class
Public Class Match
Dim m_player_1 As Player, m_player_2 As Player
Public Sub New(ByVal player_1 As Player, ByVal player_2 As Player)
m_player_1 = player_1
m_player_2 = player_2
End Sub
Public ReadOnly Property Player1() As Player
Get
Return m_player_1
End Get
End Property
Public ReadOnly Property Player2() As Player
Get
Return m_player_2
End Get
End Property
Public Overrides Function ToString() As String
Return String.Format("Match {0} vs. {1}", Player1, Player2)
End Function
End Class
Edit 1:
An alternate random sorter (which should be faster) is
Public Function RandomSortItems(Of T)(ByVal items As T()) As T()
Dim slist As New SortedList(Of Double, T)
For i As Integer = 1 to items.Length
slist.Add(rnd.NextDouble(), items(i-1) )
Next i
return slist.Values.ToArray()
End Function

How to merge two list to have a distinct list without duplicate values in vb.net

I have this problem in vb.net. Lets say I got 2 Lists ListA and ListB both holds objects of same type.
Eg., one of the property of the object is ID. (ID is written in brackets)
ListA ListB
---------------------------
A(3818) A(3818)
B(3819) B(3819)
C(3820) C(3820)
D(3821) D(3821)
E(3823) F(0)
H(3824) G(0)
I(3825)
How do I merge these two Lists to have a new distinct list which holds objects only once whose ID matches and all other objects(whose ID dont match) are simply added to the new list.
Sample output be,
New List
--------
A(3818)
B(3819)
C(3820)
D(3821)
E(3823)
F(0)
G(0)
H(3824)
I(3825)
When I searched I found that AddRange() and Union are some of the methods to do the merge. But i am not able to find if this works for non standard objects(apart from Integer, String)
Use addRange() and then linq with distinct to filter out the duplicates.
Dim b = YourCollection.Distinct().ToList()
Could use a collection bucket
Dim oCol As New Collection
AddTitems(oCol, oListA)
AddTitems(oCol, olistB)
Public Function AddTitems(oSummaryList As Collection, oList As List(Of thing)) As Collection
For Each oThing As thing In oList
If Not oSummaryList.Contains(CStr(oThing.ID)) Then oSummaryList.Add(oList, CStr(oThing.ID))
Next
Return oSummaryList
End Function
Here are a couple simple functions that should do that for you. I'm not sure how efficient they are though. I don't think there is anything built in.
Private Function nameOfFunction(list1 as list(of type), list2 as list(of type)) as list(of type)
Dim result as new list(of type)
for a as integer = 0 to math.max(list1.count, list2.count) - 1 step 1
If a < list1.count AndAlso resultHasID(result, list1(a).ID) = False Then
result.add(list1(a))
end if
If a < list2.count AndAlso resultHasID(result, list2(a).ID) = False Then
result.add(list2(a))
end if
next
End Function
Private Function resultHasID(testList as list(of type), s as string) as boolean
Dim result as Boolean = False
for a as integer = 0 to testlist.count - 1 step 1
if(testlist(a).ID = s) then
result = true
exit for
End if
Next
Return result
End function
For each item as String in ListA
If Not ListB.Contains(item) Then
ListB.Add(item)
End If
Next