'Group by and count in C# w/ Console.WriteLine

I'm trying to console.writeline my answer but I am missing something and after 2 hours of searching, I just can't figure it out.

I want my console to say

Apple Large 2
Apple Mid 1
Apple Small 2

The goal is for it to say the type, the size, group the sizes together, and state how many sizes there are of each Here's my code, any help would be appreciated thanks

namespace AppleGrouping
{
    class Program
    {
        static void Main(string[] args)
        {
            var buckets = new List<Ap>()
            {
                new Ap { Type = "Apple", Size = "Large" },
                new Ap { Type = "Apple", Size = "Small" },
                new Ap { Type = "Apple", Size = "Mid" },
                new Ap { Type = "Apple", Size = "Large" },
                new Ap { Type = "Apple", Size = "Small" },
            };

            var numberGroups = buckets.GroupBy(ap => ap.Size);
            foreach (var grp in numberGroups)
            {
                var number = grp.Key;
                var total = grp.Count();
            }

            foreach (var ap in grp)
                Console.WriteLine(ap.Type + " " + grp.Key);

        }

        public class Ap
        {
            public string Type { get; set; }

            public string Size { get; set; }
        }
    }
}


Solution 1:[1]

Your problem is here:

// This stores the data in groups, with the Size as key... 
var numberGroups = buckets.GroupBy(ap => ap.Size);

// here, you're storing key and count in temporary variables, 
// but not doing anything with them. 
foreach (var grp in numberGroups)
{
    var number = grp.Key;
    var total = grp.Count();
}

Replace the last bit with this:

foreach (var grp in numberGroups)
{
    Console.WriteLine("Apple " + grp.Key + "  " + grp.Count());
}

Solution 2:[2]

Side note, perhaps one day your list will contain more than just apples, so you'd group by the type too and print it out:

        var numberGroups = buckets.GroupBy(ap => new { ap.Size, ap.Type } );
        foreach (var grp in numberGroups)
        {
            var typeAndSize = grp.Key;
            var total = grp.Count();
            Console.WriteLine($"Type: {typeAndSize.Type}, Size: {typeAndSize.Size}, Count: {total}");
        }

The new { ap.Size, ap.Type } is a really useful device in c#; it's called an anonymous type, and it's like a mini class that the compiler writes for us so we don't have to write a dedicated one. It does some extra things that typical classes don't do, that make it really useful in a grouping situation; when GroupBy is assessing whether one class instance is equal to another it will use the Equals method. By default this comes from Object, the parent class of everything in C#. Object's Equals method returns true if the two things being compared live at the same memeory address in C#. That's useful, but if you have two different objects with the same data value but at different memory addresses (like you do with your two Large Apple) it would say they are not equal. This would mean if you just asked GroupBy to group your objects straight as they are, it wouldn't group anything because that are all considered not equal to each other. Anonymous types are considered equal if all their data is the same, so because two different instances are both "Large"and "Apple", GroupBy will count them as the same

(You could also provide your own version of Equals and GetHashCode that check two different Ap and compare their data, to achieve the same result, but for this use case it's easier to use an anonymous type)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Kjartan
Solution 2 Caius Jard