我在DataTable上使用linq(在c#中),并且想知道如何按多个字段进行分组。我发现可以使用匿名类来完成,例如
var a = dt.AsEnumerable().GroupBy(e => new { name = e["Name"] })
问题是,我的分组键是在运行时动态确定的。所以我改为尝试用词典分组:
var a = dt.AsEnumerable().GroupBy(e => GetKey(e))
其中GetKey(e)
返回Dictionary<string, object>
。这个想法是字典值取代了匿名类键和值。我的问题是linq查询不再按预期工作 - 它似乎根本不进行任何分组。我的预感是因为它在内部必须比较每个DataTable行的分组键,并且字典键不被认为是相同的,因为它们具有相同的键和值,因此每行具有不同的分组键,因此不是聚合
如果我是对的,那么解决这个问题的正确方法是什么?我尝试在类中包装字典,并覆盖Equals()方法,但它从未被调用。
答案 0 :(得分:1)
为什么不让GetKey()以字符串形式返回密钥?
var a = dt.AsEnumerable().GroupBy(e => new { name = e[GetKey(e)] });
您可以根据指定列中的值创建密钥,并将其设置为一个字符串以进行分组:
var keyDictionary = new Dictionary<string, IEnumerable<string>>();
keyDictionary.Add("Table1", new List<string> {"Group", "Position"});
var dt = new DataTable("Table1");
dt.Columns.AddRange(new [] { new DataColumn("Id", typeof(int)), new DataColumn("Group", typeof(string)), new DataColumn("Position", typeof(string)), new DataColumn("Name", typeof(string))});
var rowItemArrays = new [] { new object[] { 1, "Alpha", "Left", "Bob" }, new object[] { 2, "Alpha", "Right", "Mary"}, new object[] { 3, "Beta", "Right", "Bill"}, new object[] { 4, "Alpha", "Right", "Larry"}};
rowItemArrays.ToList().ForEach(i => dt.Rows.Add(i));
Func<DataRow, string> GetKeys = (dataRow) => string.Join("", keyDictionary[dataRow.Table.TableName].Select(key => dataRow[key].ToString()).ToArray());
var a = dt.AsEnumerable().GroupBy(GetKeys);
你必须注意空值等......
答案 1 :(得分:1)
这是从帮助文件和我没有实现的东西,但应该工作。问题是你需要一个类来进行比较,它在比较中同时使用ToString和GetHashCode(这就是为什么你的字典想法不起作用,它不是比较字典的元素,而是比较ToString和它的GetHashCode)。让GetKey返回以下类,并使用上面的Dictionary填充类的keyBag:
class PortableKey
{
public Dictionary<string, object> keyBag { get; set; }
public PortableKey(Dictionary<string, object> Keys)
{
this.keyBag = Keys;
}
public override bool Equals(object obj)
{
PortableKey other = (PortableKey)obj;
foreach (KeyValuePair<string, object> key in keyBag)
{
if (other.keyBag[key.Key] != key.Value) return false;
}
return true;
}
public override int GetHashCode()
{
// hashCodes is an array of integers represented as strings. { "1", "4", etc. }
string[] hashCodes = keyBag.Select(k => k.Value.GetHashCode().ToString()).ToArray();
// hash is the Hash Codes all joined in a single string. "1,4,etc."
string hash = string.Join(",", hashCodes);
// returns a single hash code for the combined hash.
// Note, this is not guaranteed unique, nor is it intended to be so.
return hash.GetHashCode();
}
public override string ToString()
{
string[] values = keyBag.Select(k => k.Value.ToString()).ToArray();
return string.Join(",", values);
}
}
答案 2 :(得分:-1)
var keyDictionary = new Dictionary<string, IEnumerable<string>>();
keyDictionary.Add("Table1", new List<string> {"Group", "Position"});
var dt = new DataTable("Table1");
dt.Columns.AddRange(new [] { new DataColumn("Id", typeof(int)), new DataColumn("Group", typeof(string)), new DataColumn("Position", typeof(string)), new DataColumn("Name", typeof(string))});
var rowItemArrays = new [] { new object[] { 1, "Alpha", "Left", "Bob" }, new object[] { 2, "Alpha", "Right", "Mary"}, new object[] { 3, "Beta", "Right", "Bill"}, new object[] { 4, "Alpha", "Right", "Larry"}};
rowItemArrays.ToList().ForEach(i => dt.Rows.Add(i));
Func<DataRow, string> GetKeys = (dataRow) => string.Join("", keyDictionary[dataRow.Table.TableName].Select(key => dataRow[key].ToString()).ToArray());
var a = dt.AsEnumerable().GroupBy(GetKeys);
这是你可以尝试我的朋友的最好的逻辑,我们有很多关于此的研究,所以我写的答案是我教授给出的逻辑