Sorting List(Of T) stopping early - vb.net

I have a custom class called SaveFile. One property it has is SaveNumber and I'm trying to sort the list by that property. I read a bunch of articles here and got it to sort properly but it's stopping early? The case in which I noticed this is with set that has 79 saves. It would sort: 1, 2, 3, 4, 5, 6, 7, 10, 11, ... 30, 8, 31, ... 70, 9, 71, .. The code I use is
saveList.Sort(Function(x, y) x.CompareTo(y))
But if I use the code:
For i = 0 To 3
saveList.Sort(Function(x, y) x.CompareTo(y))
Next
then it sorts right but it takes a very long time and one set has over 700 SaveFiles in it so it takes almost 5 minutes for my program to load. Any ideas?
This is the code that I have for my CompareTo function:
Public Function CompareTo(y As SaveFile) As Integer Implements IComparable(Of SaveFile).CompareTo
'If neither are an autosave then compare save number
If Not Me.Text.StartsWith("autosave") And Not y.Text.StartsWith("autosave") Then
If Me.SaveNumber.ToString.Length > y.SaveNumber.ToString.Length Then
Return False
ElseIf Me.SaveNumber.ToString.Length < y.SaveNumber.ToString.Length Then
Return True
End If
Return Me.SaveNumber < y.SaveNumber
'if either is an autosave
Else
'Create to comparable integers with
'a value of 4. We set the value to
'4 because it is higher than the 3
'available autosaves, making it sort
'after any autosaves if only one is
'an autosave.
Dim xComp As Integer = 4
Dim yComp As Integer = 4
'If x is an autosave then figure out
'which autosave number it is.
If Me.Text.StartsWith("autosave") Then
Select Case True
Case Me.Text.EndsWith("1")
xComp = 1
Case Me.Text.EndsWith("2")
xComp = 2
Case Me.Text.EndsWith("3")
xComp = 3
End Select
End If
'if y is an autosave then figure out
'which autosave number it Is.
If y.Text.StartsWith("autosave") Then
Select Case True
Case y.Text.EndsWith("1")
yComp = 1
Case y.Text.EndsWith("2")
yComp = 2
Case y.Text.EndsWith("3")
yComp = 3
End Select
End If
Return xComp < yComp
End If
End Function

First, asking 2 questions in one post is not a good idea because it reduces the chances someone will know the answer to both. For example, without seeing the code that loads these things or even what they are, makes it just a guess why it takes so long to load.
For the sorting, your CompareTo method is flawed. From MSDN:
Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
That is, it should return -1 (precedes), 1 (follows) or 0 (same). Yours just returns Boolean:
If Me.SaveNumber.ToString.Length > y.SaveNumber.ToString.Length Then
Return False
ElseIf Me.SaveNumber.ToString.Length < y.SaveNumber.ToString.Length Then
Return True
End If
...
Return xComp < yComp
We have no idea what type SaveNumber is, but if it is am integer, comparing the length of the string form if it is not the same as comparing the value:
' Me vs Other
Return "9".Length < "7".Length
That will return False (0) when it by value, it should return 1. Turning on Option Strict would have flagged the incorrect return type, which might have led you to the answer. It should be something like this, ignoring the "auto" logic:
Public Function CompareTo(other As SaveItem) As Integer _
Implements IComparable(Of SaveItem).CompareTo
... special handling for "auto"
If Me.SaveNumber = other.SaveNumber Then
Return 0
ElseIf Me.SaveNumber < other.SaveNumber Then
Return -1
Else
Return 1
End If
End Function
But, you may not even need that (assuming again that SaveNumber is an int):
saveItems = saveItems.OrderBy(Function(q) q.SaveNumber).ToList()
IComparable.CompareTo() might be needed in order to handle the "autosave" logic but it might be able to be handled in the OrderBy() depending on the information the class exposes.

Related

my compare statement not working in vb.net

I was doing an exam algorithm earlier (Just started vb 5 days ago so be nice ;) ) and I realised after testing that for whatever reason my statement If 0 < CInt(Num(x)) < 10 was not working which completely baffled me as I would thinkk that of all things THIS would be the easiest but no. No matter what I did, it just would always be true even if I made Num(X) = 90 or 9000000 it wouldn't matter, so if anyone knows what obvious mistake I have made whether its because I made it a string or what let me know :)
Dim Num(2), INPUT As String
Dim Cond As Boolean = False
Dim Valid As Boolean = False
Dim Isnum As Boolean
Dim x, Number, IsTen As Integer
While Cond = False
Do Until IsTen = 3 And Number = 3
IsTen = 0
Number = 0
For x = 0 To 2
MsgBox("Please enter a number")
Num(x) = (Console.ReadLine)
Isnum = IsNumeric(Num(x))
If Isnum = True Then
IsTen = IsTen + 1
End If
If Isnum = True Then
If 0 < CInt(Num(x)) < 10 Then
Number = Number + 1
End If
End If
Next
If Number <> 3 Then
MsgBox("Sorry all three numbers werent between 0 and 10, Please enter again")
End If
If IsTen <> 3 Then
MsgBox("Sorry all three inputs werent numbers, Please enter again")
End If
Loop
MsgBox("All three numbers are valid")
End While
First thing I would advise is setting Option Strict On at the top of your code file. You can also make this the default via Visual Studio options. This will warn you about the implicit type conversion that is causing your problem.
Changing the line
If 0 < CInt(Num(x)) < 10 Then
I'm assuming the intent here is to ensure the number is greater than 1 but less than 10
To something like this:
If Isnum = True Then
Dim val As Integer = Integer.Parse(Num(x))
If (val > 0) AndAlso (val < 10) Then
Number = Number + 1
End If
End If
Notice the use of Integer.Parse() to type cast the string to Int, it's a little more robust but there is also the Integer.TryParse() which is another step up again, might be worth reading up on. Also notice AndAlso usage, this is a short circuit operator so if the First condition is false, doesn't bother evaluating the next.

How to enable/disable button with a function

I have a problem with my university project
It's a little game, 6 buttons for each players and 2 players so 12 buttons
There is number in each buttons, if a player has his 6 buttons at 0, he can't play
I have try some Public Function and i'm actually working with a very simple one but i think this is not the problem
My function is here
And in my form, the problem is here, i've tried many things but i don't know how do to that ... I read my lesson and I'm searching on the internet, i have no idea ..
If possible is True you don't re-enable the button.
You can simplify things.
Public Function PeutJouer(ByVal joueur As Integer) As Boolean
Dim sum As Integer
Dim start As Integer = (joueur - 1) * 7
For i As Integer = start To start + 5
sum += tableau(i)
Next
Return sum <> 0
End Function
Then
Btn1P1.Enabled = PeutJouer(1)
Did you show all the relevant code? You are declaring Dim tableau(12) As Integer but the array is never filled with values. Probably tableau should be declared at the form level and not locally in this function. If you already have both, remove the local declaration, because it hides the one at form level. You also need to return the result from the function. I don't see this in your function.
Note that this
If x <> 0 Then
booleanVariable = True
Else
booleanVariable = True
End If
can be simplified to
booleanVariable = x <> 0
i.e., the condition is an expression yielding the Boolean result True or False already and you can use this value directly. When working with numeric values you don't write If x + y = 1 Then r = 1 Else If x + y = 2 Then r = 2 .... You simply write r = x + y.

Why won't the else statement run?

I am writing a function that checks to see if all elements in an array are equal, but for some reason, the else statement never seems to run, even when the if condition IS NOT TRUE. Any reason for this weird behavior? I actually displayed the result of the boolean expression a(i) = a(i + 1) and it was false. What could be going on here?
VB.NET Code:
Function EqualItems(ByVal a As Integer())
For i As Integer = 1 To a.Length - 1
If a(i) = a(i + 1) Then
If i + 1 = a.Length Then
Return True
End If
Else
Return False
End If
Next
End Function
There are more than a few things wrong here, I will explain in a bit...
First here's what I would do using Linq. Just make sure to Import System.Linq at the top of your class file...
Public Function IsEqual(ByVal a As Integer()) As Boolean
Return a IsNot Nothing AndAlso a.count > 0 AndAlso a.Distinct.Count() = 1
End Function
Breakdown of function
Make sure the array is not nothing.
We have more than one item so we can compare the other's in the array.
Finally do the compare of the items. The .Distinct.Count() = 1 will return a boolean of either True or False compare to all items that are in the array...
Note: Not good for comparing some objects this way...
Your Issue's
The first problem is this: For i As Integer = 1 To a.Length - 1. Should start at 0 for arrays. So it should look like this: For i As Integer = 0 To a.Length - 1.
The next is: If a(i) = a(i + 1) Then. This is where you would throw the IndexOutOfRange exception as because there might be not index at: a(i + 1). Solution: If Not (i + 1 = a.Length) Then check before trying to access that index...
You can declare a Boolean variable to False before everything. Then if anywhere the items are not equal return false or set the boolean to false and return that...
On a side note
Implement some Try Catch blocks to catch and handle the errors. Turn Option Strict On... If you had this on it would say something about the function may not return anything (cant remember what exactly that message is off-hand).

.Net Float error 4 - float(4) = 4.44089209850063E-16

I have a sql database, it has a column of floats. In it, the value is "4" and it is displayed a "4" while other values are 2.5, 3.6, all have decimals and those seem to function fine for my issue. But the number without a decimal is different.
I have a basic function
Private Function getScore(scoreValue As Double) As String
If (scoreValue = 5) Then
Return "Platinum"
ElseIf scoreValue >= 4 Then
Return "Gold"
ElseIf scoreValue >= 3 Then
Return "Silver"
ElseIf scoreValue >= 2 Then
Return "Bronze"
Else
Return "Unsatisfactory"
End If
End Function
When i pass this "4" that's a float, it returns the "silver" score. How am i to do this function correctly, i can't round the score, so i can't cast it as an integer. I did various debug statements to figure out what was going on and when i output scoreValue - 4 it yielded 4.44089209850063E-16
If you are not going to change the whole application and are focused on this method, you can use Math.Round:
scoreValue = Math.Round(scoreValue, 2, MidpointRounding.AwayFromZero)
This will round the number, but not to an integer - it should keep some meaningful scale.
Either way, you should also consider the scoreValue = 5 condition: it might also fail. See also: Comparing double values in C#

Conditionally skip cases

In a Select...Case statement, is there a way to skip cases based on a precondition?
What I'm doing now, using an incredibly stupid example:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case number
Case 0, 2, 4, 6, 8
If includeEvenNumbers Then
Console.WriteLine(number)
End If
Case 1, 3, 5, 7, 9
If includeOddNumbers Then
Console.WriteLine(number)
End If
End Select
Next
End Sub
Sometimes I'll even write my cases inside out:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case True
Case includeEvenNumbers
If number Mod 2 = 0 Then
Console.WriteLine(number)
End If
Case includeOddNumbers
If number Mod 2 <> 0 Then
Console.WriteLine(number)
End If
End Select
Next
End Sub
What I'd really like to do instead:
Private Sub PrintNumbers(includeEvenNumbers As Boolean, includeOddNumbers As Boolean)
For number As Integer = 0 To 9
Select Case number
Case 0, 2, 4, 6, 8 When includeEvenNumbers
Console.WriteLine(number)
Case 1, 3, 5, 7, 9 When includeOddNumbers
Console.WriteLine(number)
End Select
Next
End Sub
Notice that I used the When keyword, which is currently only used in Try...Catch blocks.
Can this be done? Who do I talk to to make this happen?
EDIT (1/2)
What's important is that this code would first evaluate When <expression>. Only if that evaluates to True, it would go on to evaluate the Case <expression>.
The main reason why I want to do this is because I'd like to write cases where the test condition throws an exception if the circumstances are right (or wrong, depending on how you look at it). I'd like to skip those cases if the precondition is true.
EDIT (2/2)
It's pretty clear by now that what I'm asking for is not possible in the current iteration of VB. So I searched for a place to submit feature requests to the .NET development team, then found out they have a uservoice platform.
Long story short: if you'd like to see this implemented, tap the vote button on this page: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4274712-add-when-keyword-support-to-select-case-stat
example of linq:
Dim condition As Predicate(Of Integer) = Nothing
If includeEvenNumbers Then
condition = Function(x) x Mod 2 = 0
Else
condition = Function(x) x Mod 2 = 1
End If
For Each item In Enumerable.Range(0, 8).Where(condition)
' do what ever you like here...
Next
EDIT: to make it simpler:
For Each item In Enumerable.Range(0, 8).Where(Function(x) x Mod 2 = If(includeEvenNumbers, 0, 1))
' do what ever you like here...
Next
By using an enum instead of 2 booleans you only need one if statement to accomplish a 2 condition check. Something like this:
Public Enum Include
Odd = 1
Even = 2
Both = 0
End Enum
Private Sub PrintNumbers(includenumbers As Include)
For I = 1 To 10
If includenumbers = Include.Both OrElse I Mod 2 = includenumbers Mod 2 Then
TextBox2.AppendText(I.ToString)
End If
Next
End Sub
You would call it like this:
PrintNumbers(Include.Odd)