linq按自定义类分组

时间:2009-10-15 22:08:29

标签: c# linq

我在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()方法,但它从未被调用。

3 个答案:

答案 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);

这是你可以尝试我的朋友的最好的逻辑,我们有很多关于此的研究,所以我写的答案是我教授给出的逻辑