【问题标题】:Unable to pivot C# datatable based on two columns无法基于两列旋转 C# 数据表
【发布时间】: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 所以要么使用不同的条件,要么使用满足您已经编写的条件的数据。

标签: c# .net linq datatable pivot


【解决方案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");
                    }
                }
    
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-20
      相关资源
      最近更新 更多