首页 文章

linq按自定义类分组

提问于
浏览
4

我在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查询不再按预期工作 - 它没有't seem to do any grouping at all. my hunch is that it' s因为在内部它必须比较每个DataTable行的分组键,并且字典键不被认为是相同的只是因为它们具有相同的键和值,因此每行具有不同的分组键,因此不会聚合 .

如果我是对的,那么解决这个问题的正确方法是什么?我尝试在类中包装字典,并重写Equals()方法,但它从未被调用过 .

3 回答

  • 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

    这是从帮助文件和我尚未实现的东西,但应该工作 . 问题是你需要一个类来进行比较,它在比较中同时使用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);
        }
    }
    
  • -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);
    

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

相关问题