One pattern I've found myself repeating, especially when rendering HTML, is grouping items of an enum into "sections," where each section might require special handling -- like "<ul>" and "</ul>" tags. The trouble with doing this "manually" is that doing so yields redundant code: you have to handle section breaks within a loop, and then also terminate the last section at the end of the loop.
I wrote an extension method to IEnumerable<T> Sectionize, to encapsulate this logic. This results in cleaner, non-redundant code. To use it, specify a delegate to handle the start and end of each section, and also provide a test delegate to determine whether two adjacent items in the enum are in the same section. This approach takes advantage of some nice features of the C# language: extension methods, lambda expressions, and dynamic iterators.
public static class SectionedEnumerator
{
/// <summary>
/// Provides a way to enumerate items by "section", where header and footer delegates
/// are called before and after each section, respectively.
/// </summary>
/// <typeparam name="T">The type of item to enumerate</typeparam>
/// <param name="items">The items to enumerate</param>
/// <param name="isSameSection">Tests whether the items are in the same section.</param>
/// <param name="doHeader">Callback to handle the start of a section (optional)</param>
/// <param name="doFooter">Callback to handle the end of a section (optional)</param>
/// <returns>Enumerable over same items, but with section handling</returns>
public static IEnumerable<T> Sectionize<T>(this IEnumerable<T> items,
Func<T, T, bool> isSameSection, Action<T> doHeader, Action<T> doFooter)
{
T lastItem = default(T);
bool startedSection = false;
foreach (T item in items)
{
if (!startedSection || !isSameSection(item, lastItem))
{
if (startedSection)
{
if (doFooter != null)
{
// End last section
doFooter(lastItem);
}
}
else
{
startedSection = true;
}
if (doHeader != null)
{
// Start new section
doHeader(item);
}
}
yield return item;
lastItem = item;
}
if (startedSection && doFooter != null)
{
// End final section
doFooter(lastItem);
}
}
}
Since this extension method returns an Enumerable<T> itself, you can chain calls to subdivide sections into subsections easily. In this example, I'm grouping a list of accounts by account category (e.g. retirement accounts) and account types (e.g. 401(k), IRA, etc.):
foreach (AccountSummary a in Accounts
.Sectionize(IsSameType, null, RenderTypeFooter)
.Sectionize(IsSameCategory, null, RenderCategoryFooter))
{
// Render item here
}
If you do chain Sectionize calls, keep in mind the calls must proceed from innermost section to outermost.