Можно ли сводить данные с помощью LINQ?
Я новичок в LINQ, но мне интересно, можно ли использовать LINQ для преобразования данных из следующего макета:
CustID | OrderDate | Qty
1 | 1/1/2008 | 100
2 | 1/2/2008 | 200
1 | 2/2/2008 | 350
2 | 2/28/2008 | 221
1 | 3/12/2008 | 250
2 | 3/15/2008 | 2150
В что-то вроде этого:
CustID | Jan- 2008 | Feb- 2008 | Mar - 2008 |
1 | 100 | 350 | 250
2 | 200 | 221 | 2150
5 ответов:
что-то вроде этого?
List<CustData> myList = GetCustData(); var query = myList .GroupBy(c => c.CustId) .Select(g => new { CustId = g.Key, Jan = g.Where(c => c.OrderDate.Month == 1).Sum(c => c.Qty), Feb = g.Where(c => c.OrderDate.Month == 2).Sum(c => c.Qty), March = g.Where(c => c.OrderDate.Month == 3).Sum(c => c.Qty) });
GroupBy
в Linq не работает так же, как SQL. В SQL вы получаете ключ и агрегаты (форма строки/столбца). В Linq вы получаете ключ и любые элементы как дочерние элементы ключа (иерархическая форма). Чтобы повернуть, вы должны проецировать иерархию обратно в форму строки/столбца по вашему выбору.
Я ответил:аналогичный вопрос используя метод расширения linq:
// order s(ource) by OrderDate to have proper column ordering var r = s.Pivot3(e => e.custID, e => e.OrderDate.ToString("MMM-yyyy") , lst => lst.Sum(e => e.Qty)); // order r(esult) by CustID
(+) универсальную реализацию
(- ) определенно медленнее, чем Дэвид Бможет ли кто-нибудь улучшить мою реализацию (т. е. метод делает порядок столбцов и строк)?
самый аккуратный подход для этого, я думаю, заключается в использовании поиска:
var query = from c in myList group c by c.CustId into gcs let lookup = gcs.ToLookup(y => y.OrderDate.Month, y => y.Qty) select new { CustId = gcs.Key, Jan = lookup[1].Sum(), Feb = lookup[2].Sum(), Mar = lookup[3].Sum(), };
вот немного более общий способ, как сводить данные с помощью 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 млн записей и более) или для случаев, когда конфигурация pivot не может быть жестко закодирована, я написал специальный 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 );