tsJensen

A quest for software excellence...

Extend IEnumerable<T> with ForEach<T> and Once<T> Methods

A few days ago, a fellow programmer asked me a common question. How do you print odd numbers between one integer value and another. It's a common question and the quickest solution that came to mind is the common for loop with a simple if and the mod operator.

Later I began toying with the question and in combination wanted to experiment with an alternative to the limited ForEach extension method on IList<T>. What you see below is the result of a little tinkering and tweaking along with some fluent goodness.

I considered naming my custom extesion method ForEach but decided to use something different to remind myself that it is not your grandfather's ForEach extension method. So I named it ForEvery and in the middle of playing with that, I ended up creating the Once extension method on IEnumerable<T>.

class Program
{
   static void Main(string[] args)
   {
      PrintOdds(1, 100);
      Console.ReadLine();
   }

   static void PrintOdds(int x, int y)
   {
      Console.WriteLine("Odds with traditional for and if");
      for (int i = x; i <= y; i++)
      {
         if (i % 2 != 0) Console.Write("{0}, ", i);
      }

      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine("Odds with ToList and ForEach with if");
      Enumerable.Range(x, y - x + 1)
         .ToList()
         .ForEach(i =>
         {
            if (i % 2 != 0) Console.Write("{0}, ", i);
         });

      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine("Odds with Where then ToList and ForEach");

      Enumerable.Range(x, y - x + 1).Where(n => n % 2 != 0)
         .ToList().ForEach(i =>
         {
            Console.Write("{0}, ", i);
         });

      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine("Odds with Where and custom ForEach");

      Enumerable.Range(x, y - x + 1)
         .Where(n => n % 2 != 0)
         .ForEach(i =>
         {
            Console.Write("{0}, ", i);
         });

      Console.WriteLine();
      Console.WriteLine();
      Console.WriteLine("Odds with custom ForEvery fluent");

      Enumerable.Range(x, y - x + 1)
         .ForEvery(n => n % 2 != 0, i => 
         { 
            Console.Write("{0}, ", i);
         })
         .Once(n => n.Count() > 99, i =>
         {
            Console.WriteLine();
            Console.WriteLine("Once fluent");
            Console.WriteLine();
            Console.WriteLine("Evens with ForEvery fluent from {0} to {1}", i.Min(), i.Max());
         })
         .ForEvery(n => n % 2 == 0, i =>
         {
            Console.Write("{0}, ", i);
         });
   }
}

public static class EnumerableExtensions
{
   public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
   {
      foreach (var item in collection)
      {
         action(item);
      }
   }

   public static IEnumerable<T> Once<T>(this IEnumerable<T> collection, 
      Func<IEnumerable<T>, bool> predicate, Action<IEnumerable<T>> action)
   {
      if (predicate(collection)) action(collection);
      return collection;
   }

   public static IEnumerable<T> ForEvery<T>(this IEnumerable<T> collection, 
      Func<T, bool> predicate, Action<T> action)
   {
      foreach (var item in collection)
      {
         if (predicate(item)) action(item);
      }
      return collection;
   }
}

Drop the code into a console app and run it. If you like these little extensions, I'd love to hear from you.