|
-
Nov 29th, 2012, 12:37 AM
#1
Thread Starter
Junior Member
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!
-
Nov 29th, 2012, 12:38 AM
#2
Thread Starter
Junior Member
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).
-
Nov 29th, 2012, 12:48 AM
#3
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
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Nov 29th, 2012, 12:50 AM
#4
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?
-
Nov 29th, 2012, 12:55 AM
#5
Re: VS2010 Finding the mode of a set of user inputted integers
 Originally Posted by ninjAl420
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.
 Originally Posted by ninjAl420
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.
 Originally Posted by .paul.
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.
-
Nov 29th, 2012, 12:58 AM
#6
Thread Starter
Junior Member
Re: VS2010 Finding the mode of a set of user inputted integers
jmcilhinney, the number of values will vary.
-
Nov 29th, 2012, 12:59 AM
#7
Thread Starter
Junior Member
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.
-
Nov 29th, 2012, 01:00 AM
#8
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.
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Nov 29th, 2012, 01:04 AM
#9
Re: VS2010 Finding the mode of a set of user inputted integers
have you learned list(of integer) or list(of decimal)?
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Nov 29th, 2012, 01:06 AM
#10
Thread Starter
Junior Member
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.
-
Nov 29th, 2012, 01:07 AM
#11
Thread Starter
Junior Member
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?
-
Nov 29th, 2012, 01:15 AM
#12
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
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Nov 29th, 2012, 01:25 AM
#13
Thread Starter
Junior Member
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.
-
Nov 29th, 2012, 03:17 AM
#14
Thread Starter
Junior Member
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".
-
Nov 29th, 2012, 03:48 AM
#15
Thread Starter
Junior Member
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.
-
Nov 29th, 2012, 04:31 AM
#16
Thread Starter
Junior Member
Re: VS2010 Finding the mode of a set of user inputted integers
-
Nov 29th, 2012, 02:21 PM
#17
Thread Starter
Junior Member
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?
-
Nov 29th, 2012, 05:36 PM
#18
Thread Starter
Junior Member
Re: VS2010 Finding the mode of a set of user inputted integers
-
Sep 30th, 2015, 08:17 PM
#19
New Member
Re: VS2010 Finding the mode of a set of user inputted integers
 Originally Posted by .paul.
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 ;~;
-
Sep 30th, 2015, 08:34 PM
#20
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
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Sep 30th, 2015, 09:44 PM
#21
New Member
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!
-
Oct 1st, 2015, 11:17 AM
#22
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.
-
Oct 1st, 2015, 11:28 AM
#23
New Member
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)
-
Oct 1st, 2015, 11:44 AM
#24
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...
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Oct 1st, 2015, 11:51 AM
#25
New Member
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
-
Oct 1st, 2015, 01:19 PM
#26
Re: VS2010 Finding the mode of a set of user inputted integers
 Originally Posted by .paul.
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.
-
Oct 1st, 2015, 02:17 PM
#27
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...
- Coding Examples:
- Features:
- Online Games:
- Compiled Games:
-
Oct 1st, 2015, 03:48 PM
#28
New Member
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
-
Oct 1st, 2015, 04:39 PM
#29
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.
-
Oct 1st, 2015, 04:43 PM
#30
New Member
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|