Fastest way to detect duplicate numbers on array vb.net 2005 - vb.net

I have this project that let user inputs 5 different numbers from 1 to 50. But I want to validate it before saving to DB that i will be 5 unique numbers. What's the best and fastest way to do this?

You can use HashSet(Of T) to check this:
Dim numbers As IEnumerable(Of Integer) = GetInputFromUser()
Dim hash As HashSet(Of Integer) = new HashSet(Of Integer)(numbers)
Dim unique As Boolean = hash.Count = numbers.Count()
This will be much more efficient than options requiring a sort + iteration.

Check this code
Private Function HasDuplicates(ByVal arr As Array) As Boolean
For i As Integer = 0 To arr.Length - 1
If Not arr(i) Is Nothing Then
Dim l As Integer = Array.LastIndexOf(arr, arr(i))
If l <> i Then Return True
End If
Next
Return False
End Function

Reed Copsey's suggestion to use hash sets was a good one (I hadn't worked with the HashSet class before).
But I then discovered that the IEnumerable class offers an extension method called Distinct that copies the unique values from a source IEnumerable to a target IEnumerable.
Borrowing the first line of Reed's sample code, here's the new sample VB.NET code:
Dim numbers As IEnumerable(Of Integer) = GetInputFromUser()
Dim isUnique As Boolean = (numbers.Distinct.Count = numbers.Count)
The variable isUnique is True if the numbers IEnumerable contains no duplicate values, or False if it contains one or more duplicate values.

Put in an array, sort it and check if elements 1,2 2,3 3,4 and 4,5 are different (in a loop).

Pseudocode:
integer numbers[50]
zeroarray(numbers, 50)
integer count = 0;
while (count < 5)
{
integer value = getinput()
if (value >= 1 and value <= 50)
if (numbers[value] = 0)
{
count = count + 1
numbers[value] = 1
}
else
reject duplicate
else
reject invalid
}

You can try this very simple method:
Filtering Arrays using LINQ

To simplifiy lets say the user inputs 5 different numbers from 0 to 49.
You can create a Boolean Array IsUsed(49) with 50 elements.
Then when the user input the value iInputNum=30 you can set IsUsed(30)=TRUE.
Next time, when the user input the second value iInputNum=7, you can set IsUsed(7)=TRUE
In this way you can check in a very fast way if the number was already inserted.
if IsUsed(iInputNum) then
'Skip the Input (put the right code here)
else
'Accept the number
IsUsed(iInputNum)=TRUE
'save iInputNum in the database
end if
Do not forget to clear the array after inserting all 5 numbers.
Remenber to put the right index in order to handle the number 1-50 (e not 0-49)

Here's an alternate solution, not sure how it compares, efficiency wise, to the other solutions, but it seems to work (uses LINQ).
Dim numbers As List<int> = getListOfNumbers()
Dim allUnique As Boolean = numbers.Distinct().Count() = numbers.Count()

Very late to the party, but what about something like this (C#, sorry)?
byte[] hits = new byte[51];
byte[] entries = new byte[] { 1, 12, 12, 32, 26, 49 };
foreach (var entry in entries)
{
hits[entry]++;
}
The elements in the hits array are automatically initialized to 0. When the foreach loop is complete, hits will contain the occurrence count for every number in entries. If any is greater than 1, you have dupes.

Related

Find matches in a card hand turns up slightly off results around 10% of the time

This is my code which is supposed to compare the values in the array 'arrHands' which stores a hand of x cards (x = cardsDrawn) as singles where the integer part is the suit (1 to 4) and the decimal represents the card number ( .01 = 1 = Ace, etc).
However around 1 in 10 times it runs it returns values that are off by one or two pairs. I know that this will happen when the hand contains a three-of-a-kind, as i haven't written the code for that yet, but it still doesn't make sense. if the value returned is wrong it is always higher than i expected.
here's the code:
Dim numPairs As Integer = 0
Dim A As Integer = 1
Dim B As Integer = 1
'A and B represent the position in the array of the cards being compared
For A = 1 To cardsDrawn
For B = 1 To cardsDrawn
If (A <> B) And (A < B) And (arrHand(A) <> 0) And (arrHand(B) <> 0) Then
'The above line stops cards from being compared to each other, or to a card they have already been compared to.
If (arrHand(A) - (Int(arrHand(A))) = (arrHand(B) - (Int(arrHand(B))))) Then
'the code above extracts the card number from the single that each card is stored as
numPairs += 1
arrHand(A) = 0
arrHand(B) = 0
End If
End If
Next
Next
Thanks for any help or ideas you may have.
It is almost always a bad idea to glue 2 pieces of information into one variable. Classes make it easy to track the various datum for a card:
Public Class Card
Private Shared Faces() As String = {"Jack", "Queen", "King", "Ace"}
Private Shared Names() As String = {"Ace", "Deuce", ..."King"}
Public Property Value As Int32
Public Property Rank As Int32
Public Property Suit As String
Public Property Img As Image
Public Property Name As String
Public Overrides Function ToString() As String
Return String.Format("{0} of {1}", Names(Rank - 1), Suit)
End Function
End Class
There is a Value and a Rank because a card always has the same rank, but depending on the game, the Value may change (e.g. Euchre and Baccarat). A Deck class could use a Stack(Of Card) and a hand can be a List(Of Card) or an array depending on the game.
Given an array of 5 cards, ranking the hand for poker is simple with linq (and assuming Poker):
Dim pairs = cards.
GroupBy(Function(v) v.Value,
Function(key, values) New With {
Key .Rank = key,
Key .Count = values.Count()
}).
OrderByDescending(Function(o) o.Count).
ThenByDescending(Function(r) r.Rank).
ToArray()
If pairs.Count is 4, there is Four of a Kind; likewise 2pair when Count=2 and a pair when the count is one. If pairs(0).Count = 3 then you have trips.
If pairs.Count = 2 AndAlso pairs(0).Count = 3, then you have a FullHouse. It is pretty simple to also do a Group on the suit to determine a flush, and put them in order to see if it is a Straight.
Do be sure to test hands from high to low: you don't want to return 2 pair when it is really a Full House.

For Each Dictionary loop in Index order

I have tried my hand using for loop with Dictionary but couldn't really achieve what I want to.
I have a certain variable SomeVariable and on the value of this variable I want my foreach to work. SomeVariable can be 1,2,3 or 4
So lets say SomeVariable is 1 I want to retrieve the last item.value from among the first 3 indexes(0,1,2) inside the SomeCollection.
And if SomeVariable is 2 I want to retrieve the last item.value from among the next 3 indexes(3,4,5) inside the SomeCollection.
And so on...
For Each item As KeyValuePair(Of String, Integer) In SomeCollection
If SomeVariable = 1 Then
//....
ElseIf SwitchCount = 2 Then
//....
End If
Next
A dictionary has no defined order, so any order you perceive is transient. From MSDN:
The order of the keys in the .KeyCollection is unspecified, but it is the same order as the associated values in the .ValueCollection returned by the Values property.
Trying to use the Keys collection to determine the order shows how it is transient:
Dim myDict As New Dictionary(Of Integer, String)
For n As Int32 = 0 To 8
myDict.Add(n, "foo")
Next
For n As Int32 = 0 To myDict.Keys.Count - 1
Console.WriteLine(myDict.Keys(n).ToString)
Next
the output prints 0 - 8, in order, as you might expect. then:
myDict.Remove(5)
myDict.Add(9, "bar")
For n As Int32 = 0 To myDict.Keys.Count - 1
Console.WriteLine(myDict.Keys(n).ToString)
Next
The output is: 0, 1, 2, 3, 4, 9 (!), 6, 7, 8
As you can see, it reuses old slots. Any code depending on things to be in a certain location will eventually break. The more you add/remove, the more unordered it gets. If you need an order to the Dictionary use SortedDictionary instead.
You can't access the dictionary by index but you can access the keys collection by index. You don't need a loop for this at all.
So something like this.
If SomeVariable = 1 Then
Return SomeCollection(SomeCollection.Keys(2))
ElseIf SomeVariable = 2 Then
...
End If
If it is truly structured you could do this:
Return SomeCollection(SomeCollection.Keys((SomeVariable * 3) - 1))
You probably need some error checking and ensuring that the length of the dictionary is correct but this should put you on the right track.
You can always use a generic SortedDictionary, I only use C# so here's my example:
SortedDictionary<int,string> dict = new SortedDictionary<int, string>();
foreach( KeyValuePair<int,string> kvp in dict) { ... }

Splitting a string into a dictionary VB.net

Here's my string:
gamename\jbnightfire\hostname\testserver\hostport\26015
I'm trying to split this string into a dictionary(of string, string). Please note that this string is not static and could contain many more keys and values.
The dictionary would contain the following:
key = gamename, value = jbnightfire
key = hostname, value = testserver
key = hostport, value = 26015
I've tried at least 5 different methods and can't seem to get any that work. It should be extremely simple, but for the life of me I cannot get it to work. Any help would be greatly appreciated. Thanks.
Split the string and loop through it with a step of 2 to advance 2 items at a time. That means the key would be at index i, and i + 1 would be the value.
Dim input = "gamename\jbnightfire\hostname\testserver\hostport\26015"
Dim split = input.Split(New String() { "\" },
StringSplitOptions.RemoveEmptyEntries)
Dim dict As New Dictionary(Of String, String)
For i As Integer = 0 To split.Length - 1 Step 2
dict.Add(split(i), split(i + 1))
Next
Note that the above approach uses the Add method, which will throw an exception if a duplicate key exists. Use the above approach if you don't expect duplicates.
Alternately, you may assign the value directly to key, in which case it will overwrite any existing value if the key exists:
dict(split(i)) = split(i + 1)
Another option is to use ContainsKey and skip it if it exists, or perform some additional logic to determine whether to skip it or keep it. For example, this will skip it if it already exists:
For i As Integer = 0 To split.Length - 1 Step 2
If dict.ContainsKey(split(i)) Then Continue For
dict.Add(split(i), split(i + 1))
Next

VisualBasic 2010 - merge 2 strings and read them as a variable

i have this code, and i want to access some variables.
Dim k1 as String = "Something"
Dim k2 as String = "Something"
... to k230
------------------Then i have this:
Dim rnd = New Random()
Dim nextValue = rnd.Next(230)
For i = 0 To 230
If nextValue = i Then
MsgBox('k+i') <--BUT READ THIS AS A VARIABLE.
End If
i = i + 1
Next
i readed some similar questions, but them doesn't apply to this case.
Consider using arrays here:
http://msdn.microsoft.com/en-us/library/vstudio/wak0wfyt.aspx
An array is a set of values that are logically related to each other,
such as the number of students in each grade in a grammar school.
By using an array, you can refer to these related values by the same
name, and use a number that’s called an index or subscript to tell
them apart. The individual values are called the elements of the
array. They’re contiguous from index 0 through the highest index
value.
Try using a Dictionary:
Dim k As New Dictionary(Of Integer, String)()
k.Add(1, "Something")
k.Add(2, "Something")
'... to 230
Messagebox.Show(k(i))

Getting the index of the largest integer in an array

I have an array of integers and I need to know the index of the largest number (not the actual value, just the index of whichever is highest).
However, if one or more indexes "tie" for the highest value, I need to have all of the indexes that share that high value.
I assume this function would need to return an array (since it could be one or more indexes), but I am not totally sure how to go about getting the more efficient solution.
If this is going to be a common thing you could write your own Extension. You should add some additional sanity/null checking but this will get you started:
Module Extensions
<System.Runtime.CompilerServices.Extension()> Function FindAllIndexes(Of T)(ByVal array() As T, ByVal match As Predicate(Of T)) As Integer()
''//Our return object
Dim Ret As New List(Of Integer)
''//Current array index
Dim I As Integer = -1
''//Infinite loop, break out when we no more matches are found
Do While True
''//Loop for a match based on the last index found, add 1 so we dont keep returning the same value
I = System.Array.FindIndex(array, I + 1, match)
''//If we found something
If I >= 0 Then
''//Append to return object
Ret.Add(I)
Else
''//Otherwise break out of loop
Exit Do
End If
Loop
''//Return our array
Return Ret.ToArray()
End Function
End Module
Then to call it:
Dim ints As Integer() = New Integer() {1, 2, 8, 6, 8, 1, 4}
Dim S = ints.FindAllIndexes(Function(c) c = ints.Max())
''//S now holds 2 and 4
If you are using .NET 3.5, you can use the Max() Extension function to easily find the highest value, and use Where to locate the matching records in your source array.
IList has an IndexOf member, which helps. This code is completely untested, and probably has at least one off-by-one error.
Public Function GetPostionsOfMaxValue(ByVal input() As Integer) As Integer()
Dim ints = New List(Of Integer)(input)
Dim maxval = ints.Max
Dim indexes As New List(Of Integer)
Dim searchStart As Integer = 0
Do Until searchStart >= ints.Count
Dim index = ints.IndexOf(maxval, searchStart)
If index = -1 Then Exit Do
indexes.Add(index)
searchStart = index + 1
Loop
Return indexes.ToArray
End Function