Tuesday 30 May 2017

LINQ 07 Group By

Now let's say that we have a collection of ints but we want to group that collection say by even and odd numbers.

using System;
using System.Linq;

namespace pc.linq06
{
    class Program
    {
        static void Main(string[] args)
        {
            var nums = Enumerable.Range(0, 20);

            var groups1 = from n in nums
                          group n by (n % 2 == 0 ? "Even" : "Odd");

            var groups2 = nums.GroupBy(n => n % 2 == 0 ? "Even" : "Odd");


            foreach (var group in groups1) {
                Console.Write($"\n{group.Key}\t" );
                Array.ForEach(group.ToArray(), n => Console.Write(n + ", "));
            }
            Console.WriteLine();

            foreach (var group in groups2) {
                Console.Write($"\n{group.Key}\t");
                Array.ForEach(group.ToArray(), n => Console.Write(n + ", "));
            }
            Console.WriteLine();
        }
    }
}

When using the group by functionality our result is a collection of groups, now the above is a simple example where we use a func<int,string> for our grouping function, this is to give our groupings a friendly name.

if we use a more complex object in our collection we can just leverage that objects property values for our group key, so below we'll create two groups one for last names one for first names.

using System;
using System.Linq;

namespace pc.linq07
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Person(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var ppl = new Person[] { new Person("Pawel", "Chooch"),
                new Person("Magda", "Tyvoniuk"), new Person("Jake", "Smith"),
                new Person("Adam", "Chooch"), new Person("Pawel","Smith"),
                new Person("Tomek", "Chooch"), new Person("Adam","Smith"),
                new Person("Jake","Tyvoniuk")};

            var LastNames = from p in ppl
                            group p by p.LastName;

            var FirstNames = ppl.GroupBy(p => p.FirstName);

            Console.WriteLine("Last Names:");
            foreach (var lngroup in LastNames) {
                Console.WriteLine(lngroup.Key);
                Array.ForEach(lngroup.ToArray(), p => Console.WriteLine($"\t{p.FirstName}"));
            }

            Console.WriteLine("\nFirst Names:");
            foreach (var lngroup in FirstNames) {
                Console.WriteLine(lngroup.Key);
                Array.ForEach(lngroup.ToArray(), p => Console.WriteLine($"\t{p.LastName}"));
            }
        }
    }
}


so now we group our people by last name and by first name in our two groups, to drive the the grouping keys let's do one more iteration, where we group by name lengths

using System;
using System.Linq;

namespace pc.linq07
{
    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Person(string FirstName, string LastName)
        {
            this.FirstName = FirstName;
            this.LastName = LastName;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var ppl = new Person[] { new Person("Pawel", "Chooch"),
                new Person("Magda", "Tyvoniuk"), new Person("Jake", "Smith"),
                new Person("Adam", "Chooch"), new Person("Pawel","Smith"),
                new Person("Tomek", "Chooch"), new Person("Adam","Smith"),
                new Person("Jake","Tyvoniuk")};

            var LastNameLengths = from p in ppl
                                  group p by GetStringLengthValue(p.LastName.Length);

            var FirstNameLengths =
                ppl.GroupBy(p => GetStringLengthValue(p.FirstName.Length));

            Console.WriteLine("Last Name lengths:");
            foreach (var lngroup in LastNameLengths) {
                Console.WriteLine(lngroup.Key);
                Array.ForEach(lngroup.ToArray(), p => Console.WriteLine($"\t{p.LastName}"));
            }

            Console.WriteLine("\nFirst Name lengths:");
            foreach (var lngroup in FirstNameLengths) {
                Console.WriteLine(lngroup.Key);
                Array.ForEach(lngroup.ToArray(), p => Console.WriteLine($"\t{p.FirstName}"));
            }
        }

        static string GetStringLengthValue(int length) {
            switch (length) {
                case 1:
                    return "One";
                case 2:
                    return "Two";
                case 3:
                    return "Three";
                case 4:
                    return "Four";
                case 5:
                    return "Five";
                case 6:
                    return "Six";
                case 7:
                    return "Seven";
                case 8:
                    return "Eight";
                default:
                    return "More than Eight";
            }
        }
    }
}


now in this iteration we actually call a function that returns a key for us.