A more efficient Loop

Hello again!

This time I came across a situation where I had to write a loop for a part of the program

I have 10 numericupdown boxes

Each one is different from each other set from 1 to 10 value

I want to be able to set a different value for each box (between 1 and 10) and have it check to see if any of the numericupdown boxes have a matching or duplicate number selected in it.

I wrote a loop that works, but I feel its not an efficient way of writing the loop, so if anyone can suggest anything better...

heres what I wrote

If NUD1.Value = NUD2.Value Then

Call errormessage()

Else

If NUD1.Value = NUD3.Value Then

Call errormessage()

Else

If NUD1.Value = NUD4.Value Then

Call errormessage()

Else

and so on... then I do the same for NUD2 and so on and so on... until ive done all 10 NUD values.

As you can see, this way got pretty lengthy!



Answer this question

A more efficient Loop

  • naveen.jaikumar

     JackG wrote:
    I wrote a loop that works, but I feel its not an efficient way of writing the loop, so if anyone can suggest anything better...
    In the general case you need to compare each combination of values -- NUD1 with NUD2, NUD1 with NUD3 etc as you've already said. 

    You can save some effort becaue you don't have to compare NUD1 with NUD2 and compare NUD2 with NUD1. You compare NUD1 against NUD2 through NUD10 and then NUD2 against NUD3 through NUD10 and so on.  That still leaves you 45 comparisons to perform.

    However, you may be able to take advantage of the extra restrictions that exist.  You know the value is limited between 1 and 10.  If the number of decimals allowed is also limited you can easily map the value to an integer which you can then use to index an array. 

    For example, lets assume that all your NUDs are configured with Minimum = 1; Maximum = 10; DecimalPlaces = 2;  You know you can turn it into an integer between 1 and 1000 by multiplying by 100.

    Declare a bool array with an upper bound of 1000 and you can use the integer to index the array.  If the array value is true, the number has already been used.  If the array value is false, you set it to true to show the number has been used.  This algorithm uses only one comparison plus some arithmetic per NUD


    Dim used(1000) As Boolean
    ______________________________________________

    ' Use Floor to ensure consistently rounding down
    index = Decimal.Floor(NUD1.Value * 100)
     
    If used(index) Then
       ' number has already been used
    Else
       used(index) = True
    End If
     

     Another interesting variation might be to set CausesValidation and run this test in the Validating event handler against sender.Value.  The same handler can validate all ten of your NUDs. 


  • Coldwar

    HI Jack,

    The following is probably what you looking for although I used textboxes instead of NumericUpDown so you would need to change it a little for the differences.

    If you create a form with 4 textboxes and a button and paste this code. It will iterate through the textboxes with the same name prefix and different indexes to confirm all the contents of all the tetxboxes with the same prefix are unique - if not then you get a return type and display a message. Give it a try and you'll be able to trace what its doing.

    Scalable by adding more textboxes with the same prefix and adjusting the last parameter on the call from 4 to whatever the maximum index value is for the group of textboxes with the same prefix.

    SO If I put textboxes 1-20 on the form I'd simply the line to

    b = ValidateTextboxesAreAllUnique(Me, "TextBox", 20)

    I think this is pretty close to what you want.

    Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim b As Boolean
    b = ValidateTextboxesAreAllUnique(Me, "TextBox", 4)
    If b = True Then
    MsgBox("Duplicates Found in the textboxes")
    End If
    End Sub

    ''' <summary>
    ''' Validate the values in a group of textboxes with the same prefix are unique
    ''' </summary>
    Function ValidateTextboxesAreAllUnique(ByVal frm As Form, ByVal TextboxPrefix As String, ByVal COunt As Integer) As Boolean
    Dim bfoundDuplicate As Boolean = False

    '//For All the textboxes in the Group with the same Prefix
    '// Textbox1, Textbox2, Textbox3
    For i = 1 To COunt
    Dim strTextBoxName As String = TextboxPrefix & i.ToString
    Dim Value As String = frm.Controls(strTextBoxName).Text

    For Each t In frm.Controls
    If TypeOf (t) Is TextBox Then
    '//If Item I'm Looking at has the same Prefix then I need to get the Index from the name to determine if its the same one
    '//I got the initial value on - if its not then I'll compare the value
    If CType(t, TextBox).Name.Substring(0, TextboxPrefix.Length) = TextboxPrefix Then
    Dim iControlIndex As Integer = CType(SplitName(CType(t, TextBox).Name), Integer)

    '//The index is not the same so I can check the value for a duplicate
    '//If they match then I've found a duplicate
    If i <> iControlIndex Then
    If Value = CType(t, TextBox).Text Then
    bfoundDuplicate = True
    Return bfoundDuplicate
    End If
    End If
    End If
    End If
    Next
    Next

    Return bfoundDuplicate
    End Function

    ''' <summary>
    ''' This will get the Numbers only from a string.
    ''' Used to get the Number index from the control name
    ''' </summary>
    Function SplitName(ByVal Fullname As String) As String
    Dim outputstring As String = ""
    For Each c As Char In Fullname
    If Char.IsNumber(c) Then outputstring = outputstring & c
    Next
    Return outputstring
    End Function

    End Class


  • Jirka Nouza

    I tried out your suggestion...

    Since i and t where not declared I set i as an integer and t as an object (not sure if that was right or not)

    I did get an error when I ran it though on line:

    Dim Value As String = CType(frm.Controls(strTextBoxName), NumericUpDown).Value

    I got a NullReferenceException was unhandled

    Object reference not set to an instance of an object... I think the prob was when i declared the t to object but was not sure what else to do with it.


  • einaros

    This is the code for NumericUpDown Controls

    Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim b As Boolean
    b = ValidateNumberUpDownAreAllUnique(Me, "NumericUpDown", 4)
    If b = True Then
    MsgBox("Duplicates Found in the textboxes")
    End If
    End Sub


    ''' <summary>
    ''' Validate the values in a group of NumericUpDown with the same prefix are unique
    ''' </summary>
    Function ValidateNumberUpDownAreAllUnique(ByVal frm As Form, ByVal TextboxPrefix As String, ByVal COunt As Integer) As Boolean
    Dim bfoundDuplicate As Boolean = False

    For i = 1 To COunt
    Dim strTextBoxName As String = TextboxPrefix & i.ToString
    Dim Value As String = CType(frm.Controls(strTextBoxName), NumericUpDown).Value

    For Each t In frm.Controls
    If TypeOf (t) Is NumericUpDown Then
    '//If Item I'm Looking at has the same Prefix then I need to get the Index from the name to determine if its the same one
    '//I got the initial value on - if its not then I'll compare the value
    If CType(t, NumericUpDown).Name.Substring(0, TextboxPrefix.Length) = TextboxPrefix Then
    Dim iControlIndex As Integer = CType(SplitName(CType(t, NumericUpDown).Name), Integer)

    '//The index is not the same so I can check the value for a duplicate
    '//If they match then I've found a duplicate
    If i <> iControlIndex Then
    If Value = CType(t, NumericUpDown).Value Then
    bfoundDuplicate = True
    Return bfoundDuplicate
    End If
    End If
    End If
    End If
    Next
    Next

    Return bfoundDuplicate
    End Function


    ''' <summary>
    ''' This will get the Numbers only from a string.
    ''' Used to get the Number index from the control name
    ''' </summary>
    Function SplitName(ByVal Fullname As String) As String
    Dim outputstring As String = ""
    For Each c As Char In Fullname
    If Char.IsNumber(c) Then outputstring = outputstring & c
    Next
    Return outputstring
    End Function

    End Class


  • A more efficient Loop