C# LINQ Grouping Data

Grouping in LINQ enables you to return partitioned sets of data from a collection based on a given key value. i.e group contacts by state:

var q = from c in contacts
   group c by c.State;

var q = contacts.GroupBy(c => c.State);

The result from a LINQ grouping operation is an enumerable sequence of groups with each group having a distinct key value

The key value is determined by the key selection expression that equates the key value for each element

The return collection from a GroupBy is:

IEnumerable<IGrouping<TKey, TElement>>;

The IGrouping interface inherits from IEnumerable<TElement> and has a key property

public interface IGrouping<TKey, TElmt> : IEnumerable<TElmt>
{
   TKey Key { get; }
}

Linq to objects returns a concrete class called grouping

To enumerate the results of a group use a nested for loop:

foreach (var group in q)
{

   // Group specific code here
   Console.WriteLine(”Group key: {0}”, group.Key);

   foreach (var element in group)
   {
      // Element specific code here
      Console.WriteLine(” - {0}”, element);
   }
}

Specifying a key to group by

The keySelecter expression can by any expression that evaluates a consistent result from the desired grouping scheme

string[] partNumbers = new string[] { ”SCW10”, ”SCW1”,”SCW2”, ”SCW11”, ”NUT10”, ”NUT1”, ”NUT2”, ”NUT11” };

var q = from pn in partNumbers
   group pn by pn.Substring(0,3);

This code will fail if any of the strings is a null the moment the substring method is attempted, therefore it’s good practice to perform a null check

Grouping by Composite Keys

To group using more than one value as the key you must specify the grouping selector cause as an anonymous type

Any element which contains the values will be copied into the correct group

var q = from c in Contact.SampleData()
   group c by new { c.LastName, c.State };

You can also use a custom class in your composit key but the class must override Equals and GetHashCode

var q = from c in Contact.SampleData()
   group c by new LastNameState { LastName = c.LastName, State = c.State };

Specifying your own Comparison Function

The default behavior when grouping is to use equals comparison for the type

You can override this behavior and specify your own grouping function by creating a class which implements interface:

public interface IEqualityComparer<T>
{
   public bool Equals(T x, T y)
   public int GetHashCode(T obj)
}

Projecting Grouped Elements into a New Type

The general GroupBy function returns the same element type as the source collection elements.

If you would like to return a different type you must use the extension method syntax and specify your own element selector.

Element selector takes an instance of the original element and returns a new type based on the supplied expression.

var q = contacts.GroupBy( c => c.State, c => c.FirstName + ” “ + c.LastName );

You can also project into an anonymous type

var q = contacts.GroupBy( c => c.State ?? ”state unknown”,
c => new
{
   Title = c.FirstName + ” “ + c.LastName,
   Email = c.Email ?? ”email address unknown”
});

How to use grouping results in the same query (Query Continuation)

You can put the grouping results into a local variable within the same query by appending the into clause after the group.

Into projects the grouping result into a local variable that you can reference throughout the rest of the query giving you access to the key values and the grouped elements

var q = from c in calls
      group c by c.Number into g
      select new
      {
         Number = g.Key,
         InCalls = g.Count(c => c.Incoming),
         OutCalls = g.Count(c => !c.Incoming),
         TotalTime = g.Sum(c => c.Duration),
         AvgTime = g.Average(c => c.Duration)
      };