Results 1 to 30 of 30

Thread: VS2010 Finding the mode of a set of user inputted integers

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    VS2010 Finding the mode of a set of user inputted integers

    Hi again everyone!

    So I am just starting to learn about arrays in Visual Basic. I have just learned how to populate in array with data and also display the data in a list box. Very basic stuff.

    My next job is to find the mode of a set of user inputted data.
    For this I know I will like to use a parallel array, and I know I will have to sort through the integers entered in to the original array, possibly place each occurrence of an integer in the parallel array temporarily till we find
    another occurrence of that integer. We do this till we find the integer whos value shows up the most.

    The only problems are
    1. I do not know how to populate an array with data that a user has inputted in to a textbox.

    2. I have very limited tools at my disposal: array.length, array.GetUpperBound, array.sort, array.reverse, I know how to loop through, I just learned the "For Each" statement.

    3. I program with: Option Explicit On
    Option Strict On
    Option Infer Off

    Since I don't even know how to populate my array with data the user has entered I don't know if I can do anything else.

    Any help would be greatly appreciated!

  2. #2

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    btw the "Mode" is the number which appears most often in a set of numbers.

    Example: in {6, 3, 9, 6, 6, 5, 9, 3} the Mode is 6 (it occurs most often).

  3. #3
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    this might help. i used a listbox, a textbox, + 3 buttons to calculate mean, median, + mode of a set of user entered numbers:

    Code:
    Public Class Form1
    
        Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
            If e.KeyCode = Keys.Enter Then
                Dim value As Decimal
                If Decimal.TryParse(TextBox1.Text, value) Then
                    ListBox1.Items.Add(value.ToString)
                End If
                TextBox1.Clear()
            End If
        End Sub
    
        Private Sub ListBox1_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDoubleClick
            Dim index As Integer = ListBox1.IndexFromPoint(e.Location)
            If index <> -1 Then
                ListBox1.Items.RemoveAt(index)
            End If
        End Sub
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            'mean
            MsgBox(ListBox1.Items.Cast(Of String).Average(Function(s) CDec(s)))
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            'median
            Dim values() As String = ListBox1.Items.Cast(Of String).OrderBy(Function(s) CDec(s)).ToArray
            If ListBox1.Items.Count Mod 2 = 1 Then 'odd count
                MsgBox(values(CInt(Math.Floor(ListBox1.Items.Count / 2))))
            Else 'even count
                MsgBox(String.Format("{0}, {1}", values(ListBox1.Items.Count \ 2 - 1), values(ListBox1.Items.Count \ 2)))
            End If
        End Sub
    
        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            'mode
            Dim values() As String = ListBox1.Items.Cast(Of String).OrderBy(Function(s) CDec(s)).ToArray
            Dim occurences() As Integer = Array.ConvertAll(values, Function(s1) values.Count(Function(s2) s2 = s1))
    
            Dim done As New List(Of String)
            Dim output As String = ""
            For x As Integer = 0 To occurences.GetUpperBound(0)
                If occurences(x) = occurences.Max AndAlso Not done.contains(values(x)) Then
                    done.Add(values(x))
                    output &= values(x) & ", "
                End If
            Next
    
            MsgBox(String.Format("{0}{1}{2}", If(done.Count = 1, "", If(done.Count = 2, "BiModal", "MultiModal")), Environment.NewLine, output.TrimEnd(","c, " "c)))
    
        End Sub
    
    End Class

  4. #4
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,344

    Re: VS2010 Finding the mode of a set of user inputted integers

    First things first, is the user going to be entering the same number of values every time or might that vary?

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,344

    Re: VS2010 Finding the mode of a set of user inputted integers

    Quote Originally Posted by ninjAl420 View Post
    So I am just starting to learn about arrays in Visual Basic. I have just learned how to populate in array with data and also display the data in a list box. Very basic stuff.
    Quote Originally Posted by ninjAl420 View Post
    I have very limited tools at my disposal: array.length, array.GetUpperBound, array.sort, array.reverse, I know how to loop through, I just learned the "For Each" statement.
    Quote Originally Posted by .paul. View Post
    MsgBox(ListBox1.Items.Cast(Of String).Average(Function(s) CDec(s)))
    Yep, that's a super idea. It's aimed right at the current level of the OP and this exercise.

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    jmcilhinney, the number of values will vary.

  7. #7

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Paul a lot of that code you are using I am unfamiliar with.

  8. #8
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    i said it might help... i.e. using user entered numbers + general theory. i didn't say copy + paste it + hand it in.
    anyone who actually does that is setting theirself up for a fall.

  9. #9
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    have you learned list(of integer) or list(of decimal)?

  10. #10

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Oh sorry I was just having trouble reading it. Ill hop on Microsofts Visual Basic database and see if I can figure out what some of that stuff does and try to reformulate it with my own knowledge. Thank you for your help Paul.

  11. #11

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    I know how to use and populate listboxes with data from an array using a loop and a straight up method. I don't know what list of integer or of decimal is exactly? Is it the same as populating listboxes with that type of data?

  12. #12
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    arrays are resizeable, but lists are made to be easily resizable.
    if you haven't learned lists, the chances are you haven't learned how to resize an array.

    here's how you'd resize an array called a() + assign a number to the last element in the array:

    Code:
    Public Class Form1
    
        Dim a() As Integer
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            If a Is Nothing Then
                ReDim a(0)
            Else
                ReDim Preserve a(a.Length)
            End If
            a(a.Length-1) = your number
        End Sub
    
    End Class

  13. #13

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Cool im gonna see what I can come up with now. ill keep everyone posted.

  14. #14

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Still nothing... This is very frustrating.. I moved on an finished another assignment dealing with two dimensional arrays, but this one still has me stumped as far as user inputted data, and then populating that data somewhere so I can sort through it and find the mode. Thank you paul, but the code you posted is beyond my understanding. Although I do understand the issue you have with providing me with something easier for me to understand. I don't know what else to do =/ The book is not very helpful in this case. Im using "Programming with Microsoft Visual Basic 2010, 5th Edition".

  15. #15

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Well this is my last post for the evening.

    This is as far as I got. I didn't figure it out. This isn't what I want to do, but it is as close to what I want to do as I can possibly get.
    Any help would be much appreciated

    Code:
        Private strValues(4) As String
    
        Private Sub btnEnter_Click(sender As System.Object, e As System.EventArgs) Handles btnEnter.Click
    
            Dim strEnteredIntegers As String = txtIntegerInput.Text
            Dim intEnteredValues As Integer
            Integer.TryParse(strEnteredIntegers, intEnteredValues)
    
            For intSub As Integer = 0 To strValues.GetUpperBound(0)
                strValues(intSub) =
                    InputBox("Integer", "Integers")
            Next intSub
    
        End Sub
    
    End Class
    I really am trying im not looking for a free ride. I only have one more hour left to work on this tomorrow and I've been at it for 4+ hours today. Even words of encouragement can help at this points :-/
    Last edited by ninjAl420; Nov 29th, 2012 at 04:31 AM.

  16. #16

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    bump

  17. #17

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Now I have this
    Code:
        Private Sub btnEnter_Click(sender As System.Object, e As System.EventArgs) Handles btnEnter.Click
    
            
            lstIntegersEntered.Items.Add(txtIntegerInput.Text)
            Dim intArray(,) As Integer
            Dim intSub As Integer
    so now I know how to add values from txtbox to list box, but not list box to array.

    Is anyone whos on today able to help me?

  18. #18

    Thread Starter
    Junior Member
    Join Date
    Nov 2012
    Posts
    19

    Re: VS2010 Finding the mode of a set of user inputted integers

    Thanks for nothing...

  19. #19
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    Quote Originally Posted by .paul. View Post
    this might help. i used a listbox, a textbox, + 3 buttons to calculate mean, median, + mode of a set of user entered numbers:

    Code:
    Public Class Form1
    
        Private Sub TextBox1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TextBox1.KeyDown
            If e.KeyCode = Keys.Enter Then
                Dim value As Decimal
                If Decimal.TryParse(TextBox1.Text, value) Then
                    ListBox1.Items.Add(value.ToString)
                End If
                TextBox1.Clear()
            End If
        End Sub
    
        Private Sub ListBox1_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListBox1.MouseDoubleClick
            Dim index As Integer = ListBox1.IndexFromPoint(e.Location)
            If index <> -1 Then
                ListBox1.Items.RemoveAt(index)
            End If
        End Sub
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            'mean
            MsgBox(ListBox1.Items.Cast(Of String).Average(Function(s) CDec(s)))
        End Sub
    
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            'median
            Dim values() As String = ListBox1.Items.Cast(Of String).OrderBy(Function(s) CDec(s)).ToArray
            If ListBox1.Items.Count Mod 2 = 1 Then 'odd count
                MsgBox(values(CInt(Math.Floor(ListBox1.Items.Count / 2))))
            Else 'even count
                MsgBox(String.Format("{0}, {1}", values(ListBox1.Items.Count \ 2 - 1), values(ListBox1.Items.Count \ 2)))
            End If
        End Sub
    
        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            'mode
            Dim values() As String = ListBox1.Items.Cast(Of String).OrderBy(Function(s) CDec(s)).ToArray
            Dim occurences() As Integer = Array.ConvertAll(values, Function(s1) values.Count(Function(s2) s2 = s1))
    
            Dim done As New List(Of String)
            Dim output As String = ""
            For x As Integer = 0 To occurences.GetUpperBound(0)
                If occurences(x) = occurences.Max AndAlso Not done.contains(values(x)) Then
                    done.Add(values(x))
                    output &= values(x) & ", "
                End If
            Next
    
            MsgBox(String.Format("{0}{1}{2}", If(done.Count = 1, "", If(done.Count = 2, "BiModal", "MultiModal")), Environment.NewLine, output.TrimEnd(","c, " "c)))
    
        End Sub
    
    End Class

    I know this is a SUPER dead thread and i apologize for bringing it back, But I'm doing this project by myself (Its not homework, I just have a statistics class and I want to build my own calculator for fun because I do enjoy learning programming) However, I copy Pasta'd your code (Not that I wanted to but because I tried doing it by myself and It wasnt working, so i wanted to test yours) and Yours didnt work either :/

    Dim values() As String = ListBox1.Items.Cast(Of String).OrderBy(Function(s) CDec(s)).ToArray
    Gives this error: "Unable to cast object of type 'System.Int32' to type 'System.String'.

    Any reason why this would do this that you can think of? I've been wracking my brain with this for 3 days now and i cant seem to pinpoint the issue.

    Thanks in advance and Sorry to bring this dead thread alive again ;~;

  20. #20
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    try this:

    Code:
    Dim values() As String = ListBox1.Items.Cast(Of Object).Select(Function(o) ListBox1.GetItemText(o)).OrderBy(Function(s) CDec(s)).ToArray

  21. #21
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    Thank you this is awesome! My only issue now is fixing a few bugs with the median and mode code but not too much of a problem I hope :/ Median when "Even" should average out the two numbers it gets, and Mode shouldnt post any numbers or say multi modal if no numbers repeat more than once :S I'm too tired to work through it now so i guess ill try and fix it tomorrow!

  22. #22
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: VS2010 Finding the mode of a set of user inputted integers

    To answer your post: you got the error because your ListBox contains Integers, rather than Strings that represent Integers. For some reason, .paul. decided the best solution would be to convert your Integers to Objects, the Objects to Strings, then parse the Integers into Decimals so they could be used as numbers. Wouldn't it be faster to just use the Integers? But then you wouldn't get to show off a fancy LINQ query! (To be fair, you can only get them as Object, but a CInt() in the OrderBy() would work. What you needed
    Code:
    yourListBox.Cast(Of Integer).OrderBy(Function(n) n).ToArray()
    Here's a lot more words. Buried within is a way to use ListBox that eliminates this problem that a lot of veterans don't seem to know. It makes things super easy, and removes most of the LINQ olympics I see people teaching as "the way". It's not "the way". It's "a waste of time".

    Directly using the Items property of a ListBox is a similar "shortcut that leads to more time spent". It means you have to do some mental juggling to keep in mind exactly what's in the ListBox, and how you convert it to and from ListBox land into the code you're writing. In this project, we want "a control that displays a list of numbers", but what we're writing is "a control that displays a list of strings we have converted from numbers that must be converted to numbers before we do math". That's clunky, and leads to UI-specific conversions that spread throughout all of the code. We can do better.

    Aside: There always has to be a little bit of glue between code and the UI. Sometimes that glue is clunky code. I don't promise what I'm about to teach removes that glue. What I promise is it makes the glue more obvious and puts it in one place, instead of spreading that glue throughout the code. That leads to code that looks less like a ball of duct tape and more like a calculator with a piece of duct tape on it. The less duct tape we use, the better.

    There's a DataSource property that can take any list-like data structure. This causes the ListBox to use that list as its data. With a little bit of UI glue, that means as you mess with the list, the ListBox will reflect it and you never have to convert back and forth between yoru data and Strings or Objects. Suppose you have a similar form with a ListBox, TextBox, and 3 buttons that add, remove, or calculate the average. Here's all of that code:
    Code:
    Public Class Form1
    
        Private _numbers As List(Of Decimal)
    
        Sub New()
            InitializeComponent()
            _numbers = New List(Of Decimal)({10, 9, 4, 5, 3})
            lstItems.DataSource = _numbers
        End Sub
    
        Private Sub btnAdd_Click(sender As System.Object, e As System.EventArgs) Handles btnAdd.Click
            Dim userInput As String = txtInput.Text
            Dim convertedValue As Decimal
            If Decimal.TryParse(userInput, convertedValue) Then
                _numbers.Add(convertedValue)
            End If
    
            txtInput.Clear()
            UpdateList()
        End Sub
    
        Private Sub btnRemove_Click(sender As System.Object, e As System.EventArgs) Handles btnRemove.Click
            Dim selectedIndex As Integer = lstItems.SelectedIndex
            If selectedIndex >= 0 Then
                _numbers.RemoveAt(selectedIndex)
            End If
    
            UpdateList()
        End Sub
    
        Private Sub btnAverage_Click(sender As System.Object, e As System.EventArgs) Handles btnAverage.Click
            Dim sum As Decimal = _numbers.Sum()
            Dim average As Decimal = sum / _numbers.Count()
            MessageBox.Show(String.Format("The average is {0}.", average))
        End Sub
    
        Private Sub UpdateList()
            lstItems.DataSource = Nothing
            lstItems.DataSource = _numbers
        End Sub
    
    End Class
    I find this much easier to read, because there's no conversions to worry about except for the one place where we convert TextBox input to numbers. Our ugly UI glue is UpdateList(). It turns out the ListBox can't update itself when you change the List(Of T) class, because that class doesn't raise events when it changes. You can sort of address this by setting the property to Nothing, then resetting it to the list. That's clunky. That's why I hid it behind a Sub: I'd rather say "UpdateList()" than "Set this random property to Nothing then to Something". It's easier to understand why I called it that way.

    But we can do better.

    DataSource wants an IList or IListSource. List(Of T) is IList, but not IListSource. A few clicks away, you can find BindingList(Of T), an IList implementation that claims it supports "data binding". The DataSource documentation didn't mention it, but it uses data binding. So if we use BindingList(Of T) instead of List(Of T), the ListBox automatically updates and we don't need to use UpdateList()!
    Code:
    Imports System.ComponentModel
    
    Public Class Form1
    
        Private _numbers As BindingList(Of Decimal)
    
        Sub New()
            InitializeComponent()
            _numbers = CreateDefaultNumbers()
            lstItems.DataSource = _numbers
        End Sub
    
        Private Function CreateDefaultNumbers() As BindingList(Of Decimal)
            Dim bindingList = New BindingList(Of Decimal)()
            bindingList.Add(10)
            bindingList.Add(8)
            bindingList.Add(7)
            bindingList.Add(20)
    
            Return bindingList
        End Function
    
        Private Sub btnAdd_Click(sender As System.Object, e As System.EventArgs) Handles btnAdd.Click
            Dim userInput As String = txtInput.Text
            Dim convertedValue As Decimal
            If Decimal.TryParse(userInput, convertedValue) Then
                _numbers.Add(convertedValue)
            End If
    
            txtInput.Clear()
        End Sub
    
        Private Sub btnRemove_Click(sender As System.Object, e As System.EventArgs) Handles btnRemove.Click
            Dim selectedIndex As Integer = lstItems.SelectedIndex
            If selectedIndex >= 0 Then
                _numbers.RemoveAt(selectedIndex)
            End If
        End Sub
    
        Private Sub btnAverage_Click(sender As System.Object, e As System.EventArgs) Handles btnAverage.Click
            Dim sum As Decimal = _numbers.Sum()
            Dim average As Decimal = sum / _numbers.Count()
            MessageBox.Show(String.Format("The average is {0}.", average))
        End Sub
    
    End Class
    If you look closely, you'll see that I had to make a tradeoff with CreateDefaultNumbers(). BindingList(Of T) is just a wrapper for other collections. If I tried to initialize it with an array as I did the List(Of T) in previous examples, it would complain that it can't add/remove items from that array. Since it has messy creation, I hid that mess behind a Sub. There's always duct tape.

    Regardless, the rest of the code is exactly what we want. The only code that has to do a conversion is the "turn a text box's input into a number" code, and there's no way to avoid conversion there. But nothing else even knows there's a ListBox! If you're clever, you might realize now you can test your math without a UI.

    A lot of veterans don't seem to know this, but it makes interacing with a ListBox orders of magnitude easier. I wish it were the first way people were taught. Doesn't it seem simpler to say _numbers(3) than CDec(lstItems.Items(3))? Which line reads better?
    Code:
    _numbers.Average()
    lstItems.Items.Cast(Of Decimal).Average()
    lstItems.Items.Cast(Of Decimal).Aggregate(Function(acc, value) acc + value)) / lstItems.Items.Count
    Look how little work we had to do to get there. The more you use this technique, the more the Items-based code looks like it only exists to show off mastery of LINQ. Some argue that using Items is easier. I think they mean instead, "I'm more used to Items."
    Last edited by Sitten Spynne; Oct 1st, 2015 at 11:24 AM.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  23. #23
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    Time to get back to the books. I have no idea what any of it means Thank you for answering though and showing other solutions I could use, Im going to save this code and disect it later once I've learned a bit more (I'm still learning basics haha)

  24. #24
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    I answered to correct the current problem the OP had while working with a previous example.
    Short of completely rewriting a new example, I gave an answer that would get some progress. I'm not saying it's the best way but the OP really should be trying to write their own program...

  25. #25
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    Which I am I think I stated before this isn't a homework assignment for me, this is just me wanting to learn the code, I was having some trouble figuring out where I went wrong or what to do next so I spent some time googling and I found this. I didnt want to copy paste code, just wanted to figure out what the difference between mine and yours was and why neither worked at first

  26. #26
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: VS2010 Finding the mode of a set of user inputted integers

    Quote Originally Posted by .paul. View Post
    I answered to correct the current problem the OP had while working with a previous example.
    Short of completely rewriting a new example, I gave an answer that would get some progress. I'm not saying it's the best way but the OP really should be trying to write their own program...
    My rework was extra credit. The part you missed is the example had Strings in the ListBox, but Hexite's project seemed to have Int32. The hint was .Cast(Of String)() complaining "Can't convert Int32 to String".

    Your response involved casting Integer to Object, using a roundabout method to get the String representation of that Integer, converting each String in that list to a Decimal temporarily so they could be sorted, then returning an array of String representations of the Integers in the original list. Sort of like answering "How do I get an integer value?" with "CInt(CType(3, Object).ToString()).ToString()". It was silly and could've been compacted:
    Code:
    theList.Items.Cast(Of Integer)().ToArray()
    But when you're in a hurry and want to show off the fastest possible solution to the problem, mistakes like that tend to crop up. I suggested the alternate approach because when you don't have to worry about casting at all you can't make those mistakes. I only just realized some of the things I posted, but they would've saved me hours over the years. I can't think of a good reason to use the Items property instead of this approach, anymore.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  27. #27
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    25,479

    Re: VS2010 Finding the mode of a set of user inputted integers

    I didn't miss that part, but I didn't put any time into reworking it...

  28. #28
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    Hmm, I'm sitting here trying to figure out how I can change this bit of code
    Code:
    Private Sub btn_Mode_Click(sender As System.Object, e As System.EventArgs) Handles btn_Mode.Click
            'Mode
            Dim values() As String = ListBox1.Items.Cast(Of Object).Select(Function(o) ListBox1.GetItemText(o)).OrderBy(Function(s) CDec(s)).ToArray
            Dim occurences() As Integer = Array.ConvertAll(values, Function(s1) values.Count(Function(s2) s2 = s1))
    
            Dim done As New List(Of String)
            Dim output As String = ""
            For x As Integer = 0 To occurences.GetUpperBound(0)
                If occurences(x) = occurences.Max AndAlso Not done.Contains(values(x)) Then
                    done.Add(values(x))
                    output &= values(x) & ", "
                End If
            Next
    
            txtMode.Text = (String.Format("{0}{1}{2}", If(done.Count = 1, "", If(done.Count = 2, "BiModal", "MultiModal")), Environment.NewLine, output.TrimEnd(","c, " "c)))
            
        End Sub
    When there is only one of each number submitted by the user it still prints as multimodal and will list every piece of data the user submitted

  29. #29
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: VS2010 Finding the mode of a set of user inputted integers

    Calculating mode involves making a tally, finding the values with the highest tally. That LINQ for occurences() looked fishy to me, but it was hard to set up a test harness, so I rewrote:
    Code:
    Dim values() As Integer = {1, 2, 1, 2, 3, 5}
    Dim occurences() As Integer = Array.ConvertAll(values, Function(s1) values.Count(Function(s2) s2 = s1))
    I wasn't sure what to expect out of it. What I got was { 2, 2, 2, 2, 1, 1 }. That took me a minute. It turns out your LINQ statement is something like this:

    "For each element of the array, produce a new array where each element is the count of values matching the element in the first array"

    You can sort of use this to calculate mode. What your occurrences array means is: "2 elements match "1"", "2 elements match "2"", ..." What you really wanted was a usable tally. There's probably a cute way to do it with LINQ. I don't get paid to be cute. I get paid to be right.

    What we might want is an array that looks like { 0, 2, 2, 1, 0, 1 }. That'd mean 0 0s, 2 1s, 2 2s, etc. That's still a little stinky, imagine what the array looks like for { 1000, 10000, 50000000 }! What we really want is a data structure that has BOTH the value and its count, something like { 1: 2, 2: 2, ... } That looks like a Dictionary. Dictionaries are like arrays, only instead of using sequential indexes they use any index. A Dictionary(Of Integer, Integer) is a natural data structure for keeping a tally. The idea is something like:
    Code:
    For each value in the array:
        Add 1 to the count for this value.
    .NET dictionaries are a pain in the butt because they insist that a key be added with Add() before you can index it, which means having to do a lot of checks with ContainsKey() before we can do anything with it. Still, this is the best tool for the job. Let's revisit:
    Code:
    Dim values() As Integer = {1, 2, 1, 2, 3, 5}
    Dim valueToCount As New Dictionary(Of Integer, Integer)()
    
    For Each value In values
        If Not valueToCount.ContainsKey(value) Then
            valueToCount.Add(value, 0)
        End If
    
        valueToCount(value) += 1
    Next
    
    For Each value In valueToCount.Keys
        Console.WriteLine("{0}: {1}", value, valueToCount(value))
    Next
    Ok, cool. We have something that gives us a mode tally, but we still have extra requirements: if everything's there just once, we need to print nothing. I think you meant "multi modal" is supposed to be when there's a tie? We'll do that and let you clean it up.

    To clarify: dictionaries consider their entries to be "key/value pairs". The "key" is what you index with, the "value" is what it returns. In our case, the "key" is the value from our array, and the "value" is the tally count. Obviously this is confusing, but it's what we have to work with. I'll try and disambiguate when I use either.

    First, we may as well do a cute LINQ trick to figure out if everything's got 1 instance:
    Code:
    Dim isAllOnes As Boolean = valueToCount.Values.All(Function(v) v = 1)
    Values will get us all the values in the dictionary, which is actually our tally counts. All() applies its function to every item in the enumerable, and returns true if the function returned true for all of them. So isAllOnes will be true if all of our key/value pairs have a value of 1, which is what happens when everything in the set has a tally of 1. Next!

    "Find the values with the highest tally" isn't something we can LINQ our way out of. Or if we can, it requires a level of trickery that hides what we're doing. Let's be straightforward, assume there's more than one, and do the traditional Maximum loop:
    Code:
    Dim highestTally As Integer = 0
    For Each tally In valueToCount.Values
        If tally > highestTally Then
            highestTally = tally
        End If
    Next
    
    Dim modePairs = valueToCount.Where(Function(kvp) kvp.Value = highestTally)
    Dim modes() As Integer = modePairs.Select(Function(kvp) kvp.Key).ToArray()
    The top part looks at each value (the count) and remembers the biggest one it's seen. When it's done, I invoke the LINQ magic I've trashed. The first line's Where() will find only the key/value pairs with the values that match our highest tally. The next line grabs the key (which is the original value) from those pairs. We'll have at least one item in modes(). I could've done it on one line. I think it looks gross. Too hard to follow? Here's how you'd do it with out LINQ:
    Code:
    ' same highestTally calculation
    
    Dim modes As New List(Of Integer)()
    For Each pair In valueToCount
        If pair.Value = highestTally Then
            modes.Add(pair.Key)
        End If
    Next
    Sometimes you might say, "Gee, the non-LINQ version looks easier to understand." That's not abnormal.

    So, at the end, we have isMultiModal and the list of highest tallies. I don't know how you want to print them. Here's a test harness I used:
    Code:
    Dim values = ListBox1.Cast(Of Integer)()
    Dim valueToCount As New Dictionary(Of Integer, Integer)()
    
    For Each value In values
        If Not valueToCount.ContainsKey(value) Then
            valueToCount.Add(value, 0)
        End If
    
        valueToCount(value) += 1
    Next
    
    Dim isAllOnes As Boolean = valueToCount.Values.All(Function(v) v = 1)
    If isAllOnes Then
        Console.WriteLine("All ones!")
        Return
    End If
    
    Dim highestTally As Integer = 0
    For Each tally In valueToCount.Values
        If tally > highestTally Then
            highestTally = tally
        End If
    Next
    
    Dim modes As New List(Of Integer)()
    For Each pair In valueToCount
        If pair.Value = highestTally Then
            modes.Add(pair.Key)
        End If
    Next
    
    If modes.Count = 1 Then
        MessageBox.Show("{0} is the mode.", modes(0))
    Else
        MessageBox.Show("Multimodal!")
    End If
    You can probably condense this with LINQ queries. I don't suggest it, it's much easier to express without fancy tricks.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  30. #30
    New Member
    Join Date
    Sep 2015
    Posts
    6

    Re: VS2010 Finding the mode of a set of user inputted integers

    On my way to taco bell then to accounting class I'll read as soon as I get a chance. I thank you for putting so much time into the responses

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width