Sunday, May 04, 2008

Credit Card Validation With LINQ

The credit card mod 10 validation algorithm is quite straightforward. Easy enough for me to have a go at writing with LINQ. This example is written as an extension method validator. Let me know if you can make this even more concise.

public ValidationProperty IsCreditCard()
{
   value.Label(label).IsNumeric().WithLengthRange(13.To(18));

   var numbers = value.Trim().Reverse().Select(c => int.Parse(c.ToString()));

   int oddSum = numbers.AtOddPositions().Sum();
   int doubleEvenSum = numbers.AtEvenPositions().SelectMany(i => new int[] { (i * 2) % 10, (i * 2) / 10 }).Sum();

   if ((oddSum + doubleEvenSum) % 10 != 0)
   {
       throw new ValidationException("{0} is not a valid credit card number".With(label));
   }

   return this;
}

Here are the AtOddPositions and AtEvenPositions extension methods:

public static IEnumerable<T> AtOddPositions<T>(this IEnumerable<T> list)
{
   bool odd = false; // 0th position is even
   foreach (T item in list)
   {
       odd = !odd;
       if (odd)
       {
           yield return item;
       }
   }
}

public static IEnumerable<T> AtEvenPositions<T>(this IEnumerable<T> list)
{
   bool even = true; // 0th position is even
   foreach (T item in list)
   {
       even = !even;
       if (even)
       {
           yield return item;
       }
   }
}

Am I going extension method crazy??