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;
}
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;
}