Ось трохи більш загальний спосіб, як перемикати дані за допомогою LINQ:
IEnumerable<CustData> s;
var groupedData = s.ToLookup(
k => new ValueKey(
k.CustID, // 1st dimension
String.Format("{0}-{1}", k.OrderDate.Month, k.OrderDate.Year // 2nd dimension
) ) );
var rowKeys = groupedData.Select(g => (int)g.Key.DimKeys[0]).Distinct().OrderBy(k=>k);
var columnKeys = groupedData.Select(g => (string)g.Key.DimKeys[1]).Distinct().OrderBy(k=>k);
foreach (var row in rowKeys) {
Console.Write("CustID {0}: ", row);
foreach (var column in columnKeys) {
Console.Write("{0:####} ", groupedData[new ValueKey(row,column)].Sum(r=>r.Qty) );
}
Console.WriteLine();
}
де ValueKey - це спеціальний клас, який представляє багатовимірний ключ:
public sealed class ValueKey {
public readonly object[] DimKeys;
public ValueKey(params object[] dimKeys) {
DimKeys = dimKeys;
}
public override int GetHashCode() {
if (DimKeys==null) return 0;
int hashCode = DimKeys.Length;
for (int i = 0; i < DimKeys.Length; i++) {
hashCode ^= DimKeys[i].GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj) {
if ( obj==null || !(obj is ValueKey))
return false;
var x = DimKeys;
var y = ((ValueKey)obj).DimKeys;
if (ReferenceEquals(x,y))
return true;
if (x.Length!=y.Length)
return false;
for (int i = 0; i < x.Length; i++) {
if (!x[i].Equals(y[i]))
return false;
}
return true;
}
}
Цей підхід може використовуватися для групування за N-розмірами (n> 2) і буде добре працювати для досить малих наборів даних. Для великих наборів даних (до 1 млн записів і більше) або для випадків, коли конфігурацію зведення неможливо жорстко кодувати, я написав спеціальну бібліотеку PivotData (безкоштовно):
var pvtData = new PivotData(new []{"CustID","OrderDate"}, new SumAggregatorFactory("Qty"));
pvtData.ProcessData(s, (o, f) => {
var custData = (TT)o;
switch (f) {
case "CustID": return custData.CustID;
case "OrderDate":
return String.Format("{0}-{1}", custData.OrderDate.Month, custData.OrderDate.Year);
case "Qty": return custData.Qty;
}
return null;
} );
Console.WriteLine( pvtData[1, "1-2008"].Value );