无法基于两列旋转 C# 数据表
Posted
技术标签:
【中文标题】无法基于两列旋转 C# 数据表【英文标题】:Unable to pivot C# datatable based on two columns 【发布时间】:2021-06-23 21:55:08 【问题描述】:我有一个包含三列的 C# 数据表。表格是这样的:
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Type", typeof(string));
dt.Columns.Add("Val", typeof(string));
dt.Rows.Add("One", "NZ", "100");
dt.Rows.Add("One", "EN", "200");
dt.Rows.Add("One", "CA", "300");
dt.Rows.Add("Two", "ZM", "400");
dt.Rows.Add("Two", "SA", "200");
dt.Rows.Add("Two", "NZ", "440");
dt.Rows.Add("Two", "EN", "990");
dt.Rows.Add("Three", "IN", "660");
dt.Rows.Add("Three", "CH", "994");
dt.Rows.Add("Three", "JP", "900");
dt.Rows.Add("Three", "CA", "50");
dt.Rows.Add("Four", "WI", "330");
旋转后的预期输出:
NZ EN CA ZM SA IN CH JP WI
One 100 200 300
Two 440 990 400 200
Three 50 660 994 900
Four 330
我想旋转 Type
列,但基于 Name
列。
我正在尝试使用以下代码对其进行旋转并出现错误:
public static DataTable PivotDataTable(DataTable table, string columnX,
params string[] columnsToIgnore)
//Create a DataTable to Return
DataTable returnTable = new DataTable();
if (columnX == "")
columnX = table.Columns[0].ColumnName;
//Add a Column at the beginning of the table
returnTable.Columns.Add(columnX);
//Read all DISTINCT values from columnX Column in the provided DataTale
List<string> columnXValues = new List<string>();
//Creates list of columns to ignore
List<string> listColumnsToIgnore = new List<string>();
if (columnsToIgnore.Length > 0)
listColumnsToIgnore.AddRange(columnsToIgnore);
if (!listColumnsToIgnore.Contains(columnX))
listColumnsToIgnore.Add(columnX);
foreach (DataRow dr in table.Rows)
string columnXTemp = dr[columnX].ToString();
//Verify if the value was already listed
if (!columnXValues.Contains(columnXTemp))
//if the value id different from others provided, add to the list of
//values and creates a new Column with its value.
columnXValues.Add(columnXTemp);
returnTable.Columns.Add(columnXTemp);
else
//Throw exception for a repeated value
throw new Exception("The inversion used must have " +
"unique values for column " + columnX);
//Add a line for each column of the DataTable
foreach (DataColumn dc in table.Columns)
if (!columnXValues.Contains(dc.ColumnName) &&
!listColumnsToIgnore.Contains(dc.ColumnName))
DataRow dr = returnTable.NewRow();
dr[0] = dc.ColumnName;
returnTable.Rows.Add(dr);
//Complete the datatable with the values
for (int i = 0; i < returnTable.Rows.Count; i++)
for (int j = 1; j < returnTable.Columns.Count; j++)
returnTable.Rows[i][j] =
table.Rows[j - 1][returnTable.Rows[i][0].ToString()].ToString();
return returnTable;
我得到的错误:'所使用的反转必须具有列类型的唯一值'
我该如何解决这个错误?
【问题讨论】:
是的,异常就在您自己的代码中。为避免这种情况,columnXValues.Contains(columnXTemp)
必须始终评估为 false
所以要么使用不同的条件,要么使用满足您已经编写的条件的数据。
【参考方案1】:
使用此扩展方法:
// pivot a DataTable to a new DataTable
// By field is row id and grouping key
// Over field creates new column names
// Value field is value for new columns
public static DataTable PivotByOverWith(this DataTable dt, string ByRowFieldName, string OverColFieldName, string WithValueFieldName)
var res = new DataTable();
if (dt.Rows.Count > 0)
var dtg = dt.AsEnumerable().GroupBy(r => r[ByRowFieldName], r => new Over = r[OverColFieldName].ToString(), With = r[WithValueFieldName] );
var hasByRow = ByRowFieldName != OverColFieldName;
if (hasByRow)
res.Columns.Add(new DataColumn(ByRowFieldName, dt.Columns[ByRowFieldName].DataType));
var valueDataType = dt.Columns[WithValueFieldName].DataType;
var colNames = dtg.SelectMany(rg => rg.Select(r => r.Over)).Distinct();
foreach (var n in colNames)
res.Columns.Add(n, valueDataType);
foreach (var rg in dtg)
var newr = res.NewRow();
if (hasByRow)
newr[ByRowFieldName] = rg.Key;
foreach (var r in rg)
newr[r.Over] = r.With;
res.Rows.Add(newr);
return res;
你可以计算你的答案:
var ans = dt.PivotByOverWith("Name", "Type", "Val");
【讨论】:
【参考方案2】:这就是我之前做过数百次的方式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
class Program
static void Main(string[] args)
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Type", typeof(string));
dt.Columns.Add("Val", typeof(int));
dt.Rows.Add("One", "NZ", 100);
dt.Rows.Add("One", "EN", 200);
dt.Rows.Add("One", "CA", 300);
dt.Rows.Add("Two", "ZM", 400);
dt.Rows.Add("Two", "SA", 200);
dt.Rows.Add("Two", "NZ", 440);
dt.Rows.Add("Two", "EN", 990);
dt.Rows.Add("Three", "IN", 660);
dt.Rows.Add("Three", "CH", 994);
dt.Rows.Add("Three", "JP", 900);
dt.Rows.Add("Three", "CA", 50);
dt.Rows.Add("Four", "WI", 330);
string[] types = dt.AsEnumerable().Select(x => x.Field<string>("Type")).Distinct().OrderBy(x => x).ToArray();
DataTable pivot = new DataTable();
pivot.Columns.Add("Name");
foreach (string type in types)
pivot.Columns.Add(type, typeof(int));
var groups = dt.AsEnumerable().GroupBy(x => x.Field<string>("Name"));
foreach (var group in groups)
DataRow newRow = pivot.Rows.Add();
newRow["Name"] = group.Key;
foreach (DataRow row in group)
newRow[row.Field<string>("Type")] = row.Field<int>("Val");
【讨论】:
以上是关于无法基于两列旋转 C# 数据表的主要内容,如果未能解决你的问题,请参考以下文章
C# Winform DataTable 怎么过滤两列不重复