CSharp - LINQ GroupBy

Introduction

The GroupBy operator is used to group elements of an input sequence.

All prototypes of the GroupBy operator return a sequence of IGrouping<K, T> elements.

IGrouping<K, T> is an interface defined as follows:

public interface IGrouping<K, T> : IEnumerable<T> {
  K Key { get; }
}

an IGrouping is a sequence of type T with a key of type K.

Prototypes

There are four prototypes we will cover.

The First GroupBy Prototype

public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector);

This prototype of the GroupBy operator returns an object that when enumerated, enumerates the input source sequence, calls the keySelector method, collects each element with its key, and yields a sequence of IGrouping<K, E> instances.

Each IGrouping<K, E> element is a sequence of elements with the same key value.

Key values are compared using the default equality comparer, EqualityComparerDefault.

GroupBy operator returns a sequence of IGrouping objects, each containing a key and a sequence of the elements from the input sequence with same key.

The order of the IGrouping instances will be in the same order that the keys occurred in the source sequence, and each element in the IGrouping sequence will be in the order that element was found in the source sequence.

The Second GroupBy Prototype

public static IEnumerable<IGrouping<K, T>> GroupBy<T, K>(
  this IEnumerable<T> source,
  Func<T, K> keySelector,
  IEqualityComparer<K> comparer);

This prototype of the GroupBy operator uses EqualityComparerDefault you provided.

The Third GroupBy Prototype

public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
        this IEnumerable<T> source,
        Func<T, K> keySelector,
        Func<T, E> elementSelector);

This prototype of the GroupBy operator will specify which part of the input element is output with the elementSelector.

The Fourth GroupBy Prototype

public static IEnumerable<IGrouping<K, E>> GroupBy<T, K, E>(
        this IEnumerable<T> source,
        Func<T, K> keySelector,
        Func<T, E> elementSelector,
        IEqualityComparer<K> comparer);

This prototype of the GroupBy operator can accept a comparer with the comparer argument, and you may output elements of a different type than the input element type using the elementSelector argument.

Exceptions

ArgumentNullException is thrown if any argument other than the comparer argument is null.

The following code are going to group our StudentOptionEntry records by id and display them.

Demo

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
class Program//from w  w w.j  a va 2  s .co m
{
    static void Main(string[] args)
    {
        StudentOptionEntry[] empOptions = StudentOptionEntry.GetStudentOptionEntries();
        IEnumerable<IGrouping<int, StudentOptionEntry>> outerSequence =
          empOptions.GroupBy(o => o.id);

        //  First enumerate through the outer sequence of IGroupings.
        foreach (IGrouping<int, StudentOptionEntry> keyGroupSequence in outerSequence)
        {
            Console.WriteLine("Option records for Student: " + keyGroupSequence.Key);

            //  Now enumerate through the grouping's sequence of StudentOptionEntry elements.
            foreach (StudentOptionEntry element in keyGroupSequence)
                Console.WriteLine("id={0} : optionsCount={1} : dateAwarded={2:d}",
                  element.id, element.optionsCount, element.dateAwarded);
        }
    }
}

class StudentOptionEntry
{
    public int id;
    public long optionsCount;
    public DateTime dateAwarded;

    public static StudentOptionEntry[] GetStudentOptionEntries()
    {
        StudentOptionEntry[] empOptions = new StudentOptionEntry[] {
      new StudentOptionEntry {
        id = 1,
        optionsCount = 2,
        dateAwarded = DateTime.Parse("1990/12/31") },
      new StudentOptionEntry {
        id = 2,
        optionsCount = 3000,
        dateAwarded = DateTime.Parse("1992/06/30")  },
      new StudentOptionEntry {
        id = 2,
        optionsCount = 3000,
        dateAwarded = DateTime.Parse("1991/01/01")  },
      new StudentOptionEntry {
        id = 3,
        optionsCount = 5000,
        dateAwarded = DateTime.Parse("1997/09/30") },
      new StudentOptionEntry {
        id = 2,
        optionsCount = 3000,
        dateAwarded = DateTime.Parse("2000/04/01")  },
      new StudentOptionEntry {
        id = 3,
        optionsCount = 7500,
        dateAwarded = DateTime.Parse("1998/09/30") },
      new StudentOptionEntry {
        id = 3,
        optionsCount = 7500,
        dateAwarded = DateTime.Parse("1998/09/30") },
      new StudentOptionEntry {
        id = 4,
        optionsCount = 2456,
        dateAwarded = DateTime.Parse("1997/12/31") },
      new StudentOptionEntry {
        id = 101,
        optionsCount = 2,
        dateAwarded = DateTime.Parse("1998/12/31") }
    };

        return (empOptions);
    }
}

Result

In the code we are enumerating through an outer sequence named outerSequence, where each element is an object implementing IGrouping containing the key and a sequence of StudentOptionEntry elements having that same key.

Related Topics