Wednesday, 31 May 2017

LINQ 08 Group By into

when we group our collections we can project those groupings into new elements, so for example we can take our grouping of odd and even numbers and create a new object that holds the key value, the corresponding values and their sum.

using System;
using System.Linq;

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

            var result1 = from n in nums
                            group n by n % 2 == 0 ? "Even" : "Odd"
                            into g select new { Key = g.Key,
                                                Nums = g,
                                                Sum = g.Sum()};

            var result2 = nums.GroupBy(n => n % 2 == 0 ? "Even" : "Odd")
                .Select(g => new {Key= g.Key, Nums = g, Sum = g.Sum() });

            foreach (var g in result1) {
                Console.WriteLine($"\n\nKey: {g.Key}");
                Console.WriteLine($"Sum: {g.Sum}");
                Array.ForEach(g.Nums.ToArray(),n => Console.Write($"{n}, "));
            }
        
            foreach (var g in result2) {
                Console.WriteLine($"\n\nKey: {g.Key}");
                Console.WriteLine($"Sum: {g.Sum}");
                Array.ForEach(g.Nums.ToArray(), n => Console.Write($"{n}, "));
            }
            Console.WriteLine("\n");
        }
    }
}


now let's take a look at our people grouping example, we can group by our name lengths, use our key, and have a collection of people with that particular name length.

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;
        }
        public override string ToString()
        {
            return $"{FirstName} {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)
                                  into g select new { Key = g.Key,
                                                      PersonCount = g.Count(),
                                                      Persons = g };

            var FirstNameLengths = ppl.GroupBy(p => GetStringLengthValue(p.FirstName.Length))
                                   .Select(g => new {Key = g.Key,
                                                     PersonCount = g.Count(),
                                                     Persons = g});

            foreach (var data in LastNameLengths) {
                Console.WriteLine($"\nLastname letter count: 
                                     {data.Key}\nPeople:{data.PersonCount}");
                Array.ForEach(data.Persons.ToArray(), 
                               p => Console.WriteLine($"\t{ p.ToString()} "));
            }

            foreach (var data in FirstNameLengths) {
                Console.WriteLine($"\nFirstname letter count: 
                                            {data.Key}\nPeople:{data.PersonCount}");
                Array.ForEach(data.Persons.ToArray(), 
                             p => Console.WriteLine($"\t{ p.ToString()} "));
            }

        }

        static string GetStringLengthValue(int l) {
            switch (l) {
                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";
                defaultreturn "More than Eight";
            }
        }
    }
}


it's a bit of a contrived example but it's just to drive the idea of grouping and projection home

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.