Home > Mobile >  Using Dictionary, How do I display multiple most common numbers using .Max c# winforms
Using Dictionary, How do I display multiple most common numbers using .Max c# winforms

Time:11-17

How do I display the common number? but if other numbers are just as common, I want to be able to display multiple.

So I have an array with a max length of 24, I can generate random number between 1-100 and sort them.

Looks something like this.. 2 8 9 10 13 19 20 38 43 47 51 55 55 59 66 67 73 84 87 87 93 95 98 100

So the most common numbers are 55 and 87, as 55 and 87 show up twice.

Here's my code..

private void buttonMode_Click(object sender, EventArgs e)
{
    int mode = 0;
    int max = 0;
    var counts = new Dictionary<int, int>();
    foreach (int value in dataArray)
    {
        if (counts.ContainsKey(value))
        {
            counts[value]  ;
        }
        else
        {
            counts.Add(value, 1);
        }
    }

    foreach(KeyValuePair<int,int> count in counts)
    {
        if (count.Value > max)
        {
            mode = count.Key;
            max = count.Value;
        }
    }
    textBoxOut1.Text = $"Mode is: {mode}";
}

This only displays the lowest common value, which using the example above would be 55 only. I've searched and by using .Max this can be done, but how?

CodePudding user response:

Offering a non-LINQ method here. We'll keep track of individual number frequencies in a Dictionary<int, int> and then also keep track of the maximum frequency we've seen so far and any numbers with that frequency. This approach will allow us to do everything in a single pass over the source data.

// Initialize the test data
int[] values = "2 8 9 10 13 19 20 38 43 47 51 55 55 59 66 67 73 84 87 87 93 95 98 100".Split(' ').Select(int.Parse).ToArray();

// Prepare our frequency tracking dictionary, max frequency count, and frequent values set
var frequencyTrack = new Dictionary<int, int>();
int maxCount = 0;
var mostFrequentValues = new HashSet<int>();

foreach (int val in values)
{
    int curCount = 1;
    // If we already know about this number then we should
    // update the frequency to existing   1
    if (frequencyTrack.TryGetValue(val, out int existingCount))
    {
        curCount = existingCount   1;
    }
    // set the frequency for this value
    frequencyTrack[val] = curCount;

    // if the frequency is higher than the previously known value
    // we have a new winner, so we should record the new frequency
    // and clear the winning list of numbers before finally adding
    // the current value
    if (curCount > maxCount)
    {
        maxCount = curCount;
        mostFrequentValues.Clear();
        mostFrequentValues.Add(val);
    }
    // if the count matches the current max then we should
    // add it as another winner
    else if (curCount == maxCount)
    {
        mostFrequentValues.Add(val);
    }
    // we don't care about lesser frequencies so we don't do anything here
}

// print the result(s)
Console.WriteLine("{0} appear(s) {1} time(s)", string.Join(", ", mostFrequentValues), maxCount);

Output:

55, 87 appear(s) 2 time(s)

Try it online

CodePudding user response:

You can use GroupBy, one for the int-value and the other for the group-sizes:

List<List<int>> maxGroups = dataArray
    .GroupBy(i => i)                           // group by int-value
    .GroupBy(g => g.Count(), g => g.ToList())  // group groups by size
    .OrderByDescending(g => g.Key)             // get biggest group
    .First()
    .ToList();

This gives you a list with two sub-lists, each contain 2 integers, one 55 and the other 87.

If you want to use your counts-dictionary, you could use this:

int maxCount = counts.Values.Max();
List<(int value, int count)> maxValueCounts = counts
    .Where(kv => kv.Value == maxCount)
    .Select(kv => (kv.Key, kv.Value))
    .ToList();

Here you get a list with the max-count values in a named tuple.

CodePudding user response:

You could use 'textBoxOut1.Text = mode ", "' in you 2nd foreach.

Hint: To make the code easier to debug and read, separate it into multiple functions : 'List CountOccurences(List list)' and 'List FindTopMostCommon(List list)' (Or what ever you like for naming).

Or short it all down to Linq as Time suggested

CodePudding user response:

You could also replace the foreach on the counts dictionary with the following code:

var modes = counts.Where(count => count.Value == counts.Values.Max());
var output = string.Join(", ", modes.Select(c => c.Key.ToString()));
textBoxOut1.Text = $"Mode is: {output}";

CodePudding user response:

In my solution you don't need to write any loops.

var dict = dataArray.GroupBy(x => x)
                .ToDictionary(x => x.Key, x => x.Count());
var max = dict.Values.Max();
var modes = dict.Where(x => x.Value == max)
                .Select(x => x.Key);
  • Related