我正在使用以下链接中的扩展方法来透视我的数据: https://techbrij.com/pivot-c-array-datatable-convert-column-to-row-linq
为了防止将来有人发现此问题并且链接失效,我将包含链接中的代码:
public static DataTable ToPivotTable<T, TColumn, TRow, TData>(
this IEnumerable<T> source,
Func<T, TColumn> columnSelector,
Expression<Func<T, TRow>> rowSelector,
Func<IEnumerable<T>, TData> dataSelector)
{
DataTable table = new DataTable();
var rowName = ((MemberExpression)rowSelector.Body).Member.Name;
table.Columns.Add(new DataColumn(rowName));
var columns = source.Select(columnSelector).Distinct();
foreach (var column in columns)
table.Columns.Add(new DataColumn(column.ToString()));
var rows = source.GroupBy(rowSelector.Compile())
.Select(rowGroup => new
{
Key = rowGroup.Key,
Values = columns.GroupJoin(
rowGroup,
c => c,
r => columnSelector(r),
(c, columnGroup) => dataSelector(columnGroup))
});
foreach (var row in rows)
{
var dataRow = table.NewRow();
var items = row.Values.Cast<object>().ToList();
items.Insert(0, row.Key);
dataRow.ItemArray = items.ToArray();
table.Rows.Add(dataRow);
}
return table;
}
请参考链接中的示例,您将获得像这样的数据透视图;
var pivotTable = data.ToPivotTable(
item => item.Year,
item => item.Product,
items => items.Any() ? items.Sum(x=>x.Sales) : 0);
我的问题是,如何在查询中包含更多行以返回例如ProductCode。.item => new {item.Product, item.ProductCode}
不起作用。.
==============编辑/ 2018年10月23日=============
假设我的数据是这个
答案 0 :(得分:1)
示例:https://dotnetfiddle.net/mXr9sh
问题似乎是从表达式中获取行名,因为它仅设计用于处理一行。可以通过以下功能解决此问题:
public static IEnumerable<string> GetMemberNames<T1, T2>(Expression<Func<T1, T2>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression != null)
{
return new[]{ memberExpression.Member.Name };
}
var memberInitExpression = expression.Body as MemberInitExpression;
if (memberInitExpression != null)
{
return memberInitExpression.Bindings.Select(x => x.Member.Name);
}
var newExpression = expression.Body as NewExpression;
if (newExpression != null)
{
return newExpression.Arguments.Select(x => (x as MemberExpression).Member.Name);
}
throw new ArgumentException("expression"); //use: `nameof(expression)` if C#6 or above
}
具有此功能后,您可以替换以下行:
var rowName = ((MemberExpression)rowSelector.Body).Member.Name;
table.Columns.Add(new DataColumn(rowName));
与此:
var rowNames = GetMemberNames(rowSelector);
rowNames.ToList().ForEach(x => table.Columns.Add(new DataColumn(x)));
此方法的一个缺点是,这些列的各种值在单个列中串联后返回。因此您需要从字符串中提取数据。
结果数据表:
(显示为JSON)
[
{
"StockId": "{ StockId = 65, Name = Milk }",
"Name": "3",
"Branch 1": "1",
"Branch 2": "0",
"Central Branch": null
},
{
"StockId": "{ StockId = 67, Name = Coffee }",
"Name": "0",
"Branch 1": "0",
"Branch 2": "22",
"Central Branch": null
}
]
完整的代码清单
using System;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using Newtonsoft.Json; //just for displaying output
public class Program
{
public static void Main()
{
var data = new[] {
new { StockId = 65, Name = "Milk", Branch = 23, BranchName = "Branch 1", Stock = 3 },
new { StockId = 65, Name = "Milk", Branch = 24, BranchName = "Branch 2", Stock = 1 },
new { StockId = 67, Name = "Coffee", Branch = 22, BranchName = "Central Branch", Stock = 22 }
};
var pivotTable = data.ToPivotTable(
item => item.BranchName,
item => new {item.StockId, item.Name},
items => items.Any() ? items.Sum(x=>x.Stock) : 0);
//easy way to view our pivotTable if using linqPad or similar
//Console.WriteLine(pivotTable);
//if not using linqPad, convert to JSON for easy display
Console.WriteLine(JsonConvert.SerializeObject(pivotTable, Formatting.Indented));
}
}
public static class PivotExtensions
{
public static DataTable ToPivotTable<T, TColumn, TRow, TData>(
this IEnumerable<T> source,
Func<T, TColumn> columnSelector,
Expression<Func<T, TRow>> rowSelector,
Func<IEnumerable<T>, TData> dataSelector)
{
DataTable table = new DataTable();
//foreach (var row in rowSelector()
var rowNames = GetMemberNames(rowSelector);
rowNames.ToList().ForEach(x => table.Columns.Add(new DataColumn(x)));
var columns = source.Select(columnSelector).Distinct();
foreach (var column in columns)
table.Columns.Add(new DataColumn(column.ToString()));
var rows = source.GroupBy(rowSelector.Compile())
.Select(rowGroup => new
{
Key = rowGroup.Key,
Values = columns.GroupJoin(
rowGroup,
c => c,
r => columnSelector(r),
(c, columnGroup) => dataSelector(columnGroup))
});
foreach (var row in rows)
{
var dataRow = table.NewRow();
var items = row.Values.Cast<object>().ToList();
items.Insert(0, row.Key);
dataRow.ItemArray = items.ToArray();
table.Rows.Add(dataRow);
}
return table;
}
public static IEnumerable<string> GetMemberNames<T1, T2>(Expression<Func<T1, T2>> expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression != null)
{
return new[]{ memberExpression.Member.Name };
}
var memberInitExpression = expression.Body as MemberInitExpression;
if (memberInitExpression != null)
{
return memberInitExpression.Bindings.Select(x => x.Member.Name);
}
var newExpression = expression.Body as NewExpression;
if (newExpression != null)
{
return newExpression.Arguments.Select(x => (x as MemberExpression).Member.Name);
}
throw new ArgumentException("expression"); //use: `nameof(expression)` if C#6 or above
}
}
答案 1 :(得分:1)
匿名类型不能作为通用参数传递。尝试将枢轴键定义为结构:
public struct PivotKey
{
public string Product;
public int ProductCode; // assuming your product codes are integers
}
通过这种方式,您可以在所有字段的相等性和哈希码方面利用struct的默认Equals
和GetHashCode
方法实现。
然后,如下定义rowSelector
:
item => new PivotKey { Product = item.Product, ProductCode = item.ProductCode}