【问题标题】:DataTable group the result in one rowDataTable 将结果分组为一行
【发布时间】:2012-10-16 13:45:50
【问题描述】:

我有一个 DataTable,想将 Name、LastName 和 Comment 分组。其余的应该在同一行。 在我的代码中,首先我将 ID 值作为标题,然后将属性值组织到每个 ID。我在这里想要的是用它们的 ID 值对相同的 Name、Lastname 和 Comment 进行分组。 我的第一个表是这样的:

ID   Name   Lastmame    Comment    Attribute
1    kiki   ha          hello      FF        
3    lola   mi          hi         AA
2    ka     xe          what       UU
2    kiki   ha          hello      SS

使用我的代码后:

     Name   Lastname    Comment   1    3    2
     kiki   ha           hello    FF
     lola   mi           hi            AA
     ka     xe           what              UU 
     kiki   ha           hello             SS

我想要的是:

     Name   Lastname    Comment   1    3    2
     kiki    ha           hello   FF        SS
     lola    mi            hi          AA
     ka      xe           what              UU   

我的代码:

DataTable table1 = new DataTable("Kunde"); 
table1.Columns.Add("Comment", typeof(String)); 
table1.Columns.Add("Name", typeof(String)); 
table1.Columns.Add("Lastname", typeof(String)); 

DataTable comment = new DataTable("Comment");
comment.Columns.Add("ID", typeof(String)); 
comment.Columns.Add("Comment", typeof(String)); 
comment.Columns.Add("Attribute", typeof(String)); 

DataSet ds = new DataSet("DataSet"); 
ds.Tables.Add(table1); 
ds.Tables.Add(comment); 

object[] o1 = { "hello", "kiki", "ha" }; 
object[] o2 = { "hi", "lola", "mi" }; 
object[] o3 = { "what", "ka", "xe" };  
object[] c1 = { 1, "hello", "FF" }; 
object[] c2 = { 3, "hi", "AA" };
object[] c3 = { 2, "what", "UU" };
object[] c4 = { 2, "hello", "SS" }; 

table1.Rows.Add(o1); 
table1.Rows.Add(o2); 
table1.Rows.Add(o3); 
comment.Rows.Add(c1); 
comment.Rows.Add(c2);
comment.Rows.Add(c3);
comment.Rows.Add(c4);

var results = from tb1 in comment.AsEnumerable() 
              join tb2 in table1.AsEnumerable() 
              on tb1.Field<string>("Comment") equals tb2.Field<string>("Comment") 
              select new 
              { 
                  ID = tb1.Field<String>("ID"),
                  Name = tb2.Field<String>("Name"),
                  Lastname = tb2.Field<String>("Lastname"),
                  Comment = tb1.Field<String>("Comment"),
                  Attribute = tb1.Field<String>("Attribute"),
              };
DataTable result = LINQToDataTable(results);
var products = result.AsEnumerable()
                    .GroupBy(c => c["ID"])
                    .Where(g => !(g.Key is DBNull))
                    .Select(g => (string)g.Key)
                    .ToList();
var newtable = result.Copy();
products.ForEach(p => newtable.Columns.Add(p, typeof(string)));

foreach (var row in newtable.AsEnumerable())
{
    if (!(row["ID"] is DBNull)) row[(string)row["ID"]] = row["Attribute"];
}
newtable.Columns.Remove("ID");
newtable.Columns.Remove("Attribute");

var result11 = from t1 in newtable.AsEnumerable()
               group t1 by new { Name = t1.Field<String>("Name"), LastName = t1.Field<String>("LastName"), Comment = t1.Field<String>("Comment"), } into grp
               select new
               {
                   Name = grp.Key.Name,
                   LastName = grp.Key.LastName,
                   Comment = grp.Key.Comment,
                   //Something here
               };

LINQToDataTable方法定义

using System.Reflection;

public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
    DataTable dtReturn = new DataTable();

    // column names 
    PropertyInfo[] oProps = null;

    if (varlist == null) return dtReturn;

    foreach (T rec in varlist)
    {
        if (oProps == null)
        {
            oProps = ((Type)rec.GetType()).GetProperties();
            foreach (PropertyInfo pi in oProps)
            {
                Type colType = pi.PropertyType;

                if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
                == typeof(Nullable<>)))
                {
                    colType = colType.GetGenericArguments()[0];
                }

                dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
            }
        }

        DataRow dr = dtReturn.NewRow();

        foreach (PropertyInfo pi in oProps)
        {
            dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
            (rec, null);
        }

        dtReturn.Rows.Add(dr);
    }
    return dtReturn;
}

【问题讨论】:

    标签: c# linq datatable linq-group


    【解决方案1】:

    基于this other answer的cmets:

    一种方法是将所有变量列填充到一个结构中(如字典)。

    为此,请使用以下查询:

    var variableColumnNames = newtable.Columns.Cast<DataColumn>()
        .Select(c => c.ColumnName)
        .Except(new[]{"Name", "Lastname", "Comment"});
    
    var result11 = from t1 in newtable.AsEnumerable()
        group t1 by new
        {
            Name = t1.Field<String>("Name"),
            LastName = t1.Field<String>("LastName"),
            Comment = t1.Field<String>("Comment"),
        } into grp
        select new
        {
            grp.Key.Name,
            grp.Key.LastName,
            grp.Key.Comment,
    
            Values = variableColumnNames.ToDictionary(
                columnName => columnName,
                columnName => grp.Max(r => r.Field<String>(columnName)))
        };
    

    如果你真的需要在类中拥有可变数量的属性,据我所知这是不可能的,所以唯一可行的方法是将结果输出到另一个DataTable(我们可以添加任意数量的列)。

    方法#2 - 使用dynamic

    LINQ 查询:

    var result11 = from t1 in newtable.AsEnumerable()
        group t1 by new
        {
            Name = t1.Field<String>("Name"),
            LastName = t1.Field<String>("LastName"),
            Comment = t1.Field<String>("Comment"),
        } into grp
        select CreateNewDynamicObject
            (
                grp.Key.Name,
                grp.Key.LastName,
                grp.Key.Comment,
                variableColumnNames.ToDictionary(
                    columnName => columnName,
                    columnName => grp.Max(r => r.Field<String>(columnName)))
            );
        }
    

    创建动态对象的新方法:

    private static dynamic CreateNewDynamicObject(
        string name, string lastName, string comment, Dictionary<string, string> customProperties)
    {
        dynamic obj = new ExpandoObject();
    
        obj.Name = name;
        obj.LastName = lastName;
        obj.Comment = comment;
    
        foreach (var prop in customProperties)
            (obj as IDictionary<string, Object>).Add(prop.Key, prop.Value ?? "");
    
        return obj;
    }
    

    方法#3 - 输出到DataTable

    生成的DataTable (destinationTable) 可用作DataGridView 的源:

    var destinationTable = new DataTable();
    
    foreach (var column in newtable.Columns.Cast<DataColumn>())
        destinationTable.Columns.Add(column.ColumnName, typeof(String));
    
    var result11 =
        from t1 in newtable.AsEnumerable()
        group t1 by new
                        {
                            Name = t1.Field<String>("Name"),
                            LastName = t1.Field<String>("Lastname"),
                            Comment = t1.Field<String>("Comment"),
                        }
            into grp
            select
                variableColumnNames.ToDictionary(
                    columnName => columnName,
                    columnName => grp.Max(r => r.Field<String>(columnName)))
                .Concat(new Dictionary<string, string>
                        {
                            {"Name", grp.Key.Name},
                            {"Lastname", grp.Key.LastName},
                            {"Comment", grp.Key.Comment}
                        }
                ).ToDictionary(x => x.Key, x => x.Value);
    
    
    foreach (var row in result11)
    {
        var newRow = destinationTable.NewRow();
    
        foreach (var columnName in newtable.Columns.Cast<DataColumn>().Select(c => c.ColumnName))
            newRow[columnName] = row[columnName];
    
        destinationTable.Rows.Add(newRow);
    }
    

    【讨论】:

    • 什么是变量列名?因为我的编译器无法识别它
    • 是的,这只是一个带有 ID 列的示例,它可以是任何字符串,如 Lastname 或 Comment 用作标题
    • @UniLe 对不起;我忘了粘贴那段代码。我现在加了。
    • 我试过你的代码,但它带有值列(集合),我做错了什么?
    • @UniLe 我添加了第三种方法(输出到DataTable)。这应该是一个很好的来源DataGridView
    猜你喜欢
    • 2012-10-06
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多