A colleague of mine asked me how to pivot a table using LINQ. I didn’t want to cast the table to an array of arrays, so I came up with the following code:
public static class TableExtensions
{
public static IEnumerable<IEnumerable> Pivot(this IEnumerable<IEnumerable> table)
{
IEnumerable first = table.FirstOrDefault();
if (first != null)
{
List enumerators = new List();
foreach (IEnumerable row in table)
{
enumerators.Add(row.GetEnumerator());
}
foreach (object element in first)
{
yield return GetColumn(enumerators);
}
}
}
private static IEnumerable GetColumn(IEnumerable enumerators)
{
foreach (IEnumerator enumerable in enumerators)
{
if (enumerable.MoveNext())
{
yield return enumerable.Current;
}
}
}
}
You can see the result when testing with the following console application:
class Program
{
private static object[][] _table = new object[][]
{
new object[]{11,12,13},
new object[]{21,22,23},
new object[]{31,32,33},
new object[]{41,42,43}
};
static void Main(string[] args)
{
PrintTable(_table);
PrintTable(_table.Pivot());
Console.ReadLine();
}
private static void PrintTable(IEnumerable<IEnumerable> table)
{
foreach (var row in table)
{
foreach (object o in row)
{
Console.Write(o.ToString() + ” “);
}
Console.WriteLine();
}
Console.WriteLine();
}
}
Now this code works fine as long as you nicely iterate over each of the columns in the correct order. You will notice the bug when doing the following:
PrintTable(_table.Skip(1));
PrintTable(_table.Pivot().Skip(1));
PrintTable(_table.Pivot().Pivot().Skip(1));
The reason for this bug is that the constructed enumerators are shared between all the “column enumerators” and their state is changed along the way. There is no efficient way to work around this problem when only using the IEnumerable interface (efficient in the sense of memory and time consumption), so you’ll have to settle with the next best thing, casting to an object array:
public static IEnumerable<IEnumerable> Pivot(this IEnumerable<object[]> table)
{
object[] first = table.FirstOrDefault();
if (first != null)
{
for (int i = 0; i < first.Count(); ++i)
{
yield return table.Select(row => row[i]);
}
}
}
Of course you can work with generics to get a strongly typed result, but sometimes the elements in every column can have another type. The tuple class in .NET 4.0 can help you with this. You just have to create a bunch of overloads like this:
public static Tuple<IEnumerable<T1>, IEnumerable<T2>, …> Pivot<T1, T2, …>(this IEnumerable<Tuple<T1, T2, …>> table)