DataTable 到 Dictionary 的转换<String,StringBuilder>

Conversion of DataTable to Dictionary<String,StringBuilder>

DataTable 具有以下列名称:

Name, Value, Type, Info

需要转换结构字典

其中 Name 是键(字符串),其他值将像 "Value,Type,Info" 一样附加到 StringBuilder,但是可能有重复的 Name 列值,然后每个附加到 StringBuilder 的值将使用像 ?: 这样的分隔符来描述下一组值。

例如:如果 DataTable 数据是这样的:

Name, Value, Type, Info

one     1     a   aa
one     11    b   bb
two     2     c   cc
two     22    dd  ddd
two     222   ee   eee

现在结果结构应该是这样的:

Dictionary<String,StringBuilder> detail = new Dictionary<String,StringBuilder>
{
{[one],[1,a,aa?:11,b,bb},
{[two],[2,c,cc?:22,dd,ddd?:222,ee,eee}
}

使用 for 循环很容易实现同样的效果,但我试图通过 Linq 来实现,所以我尝试了类似的方法:

datatable.AsEnumerable.Select(row =>
{
  KeyValuePair<String,StringBuilder> kv = new  KeyValuePair<String,StringBuilder>();

 kv.key = row[Name];
 kv.Value = row[Value]+","+row[Type]+","+row[Info]

 return kv;
}).ToDictionary(y=>y.Key,y=>y.Value)

此代码不处理重复键并因此追加,可能我需要使用 SelectMany 来展平结构,但它如何为我提供具有上述要求的字典,以便分隔符可以是添加为现有键的值。任何可以指引我正确方向的指针。

已编辑:

datatable.AsEnumerable()
          .GroupBy(r => (string)r["Name"])
          .Select(g => new
            {
                Key = g.Key,
                // Preferred Solution
                Value = new StringBuilder(
                             g.Select(r => string.Format("{0}, {1}, {2}",   
                                      r["Value"], r["Type"], r["Info"]))
                                 .Aggregate((s1, s2) => s1 + "?:" + s2))                    
                /*
                //as proposed by juharr
                Value = new StringBuilder(string.Join("?:", g.Select( r => string.Format("{0}, {1}, {2}", r["Value"], r["Type"], r["Info"]))))
                */
            })
          .ToDictionary(p => p.Key, p => p.Value);

像这样的东西应该可以工作,它避免了一些复杂的 Linq,这些 Linq 可能会刺激调试:

public static Dictionary<string, StringBuilder> GetData(DataTable table)
{
    const string delimiter = "?:";
    var collection = new Dictionary<string, StringBuilder>();

    // dotNetFiddle wasn't liking the `.AsEnumerable()` extension
    // But you should still be able to use it here
    foreach (DataRow row in table.Rows)
    {
        var key = (string)row["Name"];

        var @value = string.Format("{0},{1},{2}", 
                                   row["Value"], 
                                   row["Type"], 
                                   row["Info"]);

        StringBuilder existingSb;

        if (collection.TryGetValue(key, out existingSb))
        {
            existingSb.Append(delimiter + @value);
        }
        else
        {
            existingSb = new StringBuilder();
            existingSb.Append(@value);
            collection.Add(key, existingSb);
        }
    }

    return collection;
}