【问题标题】:Grouping by multiple keys known at runtime按运行时已知的多个键进行分组
【发布时间】:2014-11-12 14:27:56
【问题描述】:

我有一个 Telerik 网格,我将对象列表数据绑定到页面上。用户可以在查看数据时根据自己的喜好对列进行分组。我正在编写一个函数来将此数据导出到 excel 文档中,并希望保留用户在页面上的分组。我能够获取用户分组的对象属性的字符串名称。

我坚持的是如何在运行时进行分组。有很多这样的例子:Group by with multiple columns using lambda 描述了如何在编译时按多个属性分组,当您提前知道时。但是,直到在运行时,我才知道要按什么分组。

对于如何在运行时进行分组有什么建议吗?

【问题讨论】:

  • Linq 不提供按字段名称分组的方法。你必须使用表达式。但是,您可以通过字段名称在运行时构建表达式。

标签: c# .net linq


【解决方案1】:

您可能正在寻找类似this SO question 的答案,特别是:

var myData = gridData.AsEnumerable()
                     .GroupBy(r => r, new MyDataComparer(keys))
                     .ToList();

internal class MyDataComparer : IEqualityComparer<MyDataType>
{
    private readonly string[] _keys;

    public MyDataComparer(string[] keys)
    {
        _keys = keys; // keep the keys to compare by.
    }

    public bool Equals(MyDataType x, MyDataType y)
    {
        // a simple implementation that checks if all the required fields 
        // match. This might need more work.
        bool areEqual = true;
        foreach (var key in _keys)
        {
            areEqual &= (x[key].Equals(y[key]));
        }
        return areEqual;
    }

    public int GetHashCode(DataRow obj)
    {
        // Add implementation here to create an aggregate hashcode.
    }
}    

【讨论】:

    【解决方案2】:

    我会使用 Dynamic Linq (http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library)

    例子:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Dynamic; //have to install this via nuget or download it
    
    public class Person {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DOB { get; set; }
    }
    
    
    class Program {
        static void Main(string[] args) {
            var peeps = new List<Person>{
                new Person(){ FirstName="Tim", LastName="Smith", DOB = DateTime.Now},
                new Person(){ FirstName="Tim", LastName="Smith", DOB = DateTime.Now.AddDays(1)},
                new Person(){ FirstName="Mike", LastName="Smith", DOB = DateTime.Now.AddDays(2)},
                new Person(){ FirstName="Frank", LastName="Jones", DOB = DateTime.Now.AddDays(3)},
            };
    
            var eq = peeps.GroupBy("new (LastName, FirstName)", "it").Select("new(it.Key as Key, it as People)"); ;
            foreach (dynamic el in eq) {
                Console.WriteLine(el.Key);
                foreach (var person in el.People as IEnumerable<Person>) {
                    Console.WriteLine(person.LastName + " " + person.DOB);
                }
            }            
        }
    }
    

    【讨论】:

    • 我在我的代码中使用了您的建议,但我遇到了一些我不理解的行为。如果我有:systemList.GroupBy("new (" + String.Join(", ", groupFields) + ")", "it").Select("new(it.Key as Key, it as System)"); 它将正常工作,但如果我用 sys 替换它,如下所示:systemList.GroupBy("new (" + String.Join(", ", groupFields) + ")", "sys").Select("new(sys.Key as Key, sys as System)"); 我得到一个No property or field 'sys' exists in type 'SystemSummaryData' 异常。将其更改为 sys 是如何导致的?
    【解决方案3】:

    您正在尝试按运行时拥有的特定属性的名称进行分组。 它与此类似,只是它固定为 2 个过滤器。这只是您的另一种选择。 使用 linq pad 进行测试。

    过滤器的数量是固定的还是动态的?

    void Main()
    {
        var mySchool = new List<School>{
        new School{Student = "Student A", Teacher = "Teacher A", Subject = "Math", Grades = 80},
        new School{Student = "Student B", Teacher = "Teacher A", Subject = "Math", Grades = 65},
        new School{Student = "Student C", Teacher = "Teacher A", Subject = "Math", Grades = 95},
        new School{Student = "Student A", Teacher = "Teacher B", Subject = "History", Grades = 80},
        new School{Student = "Student B", Teacher = "Teacher B", Subject = "History", Grades = 100},
        new School{Student = "Student A", Teacher = "Teacher C", Subject = "English", Grades = 100},
        new School{Student = "Student C", Teacher = "Teacher B", Subject = "English", Grades = 100},
        new School{Student = "Student D", Teacher = "Teacher B", Subject = "English", Grades = 90},
        };
    
    
        GroupByFilters("Teacher", "Subject", mySchool);
        GroupByFilters("Subject", "Teacher", mySchool);
    }
    
    
    
    public void GroupByFilters(string filter1, string filter2, List<School> school)
    {
    
        PropertyInfo prop1 = typeof(School).GetProperties()
                                          .Where(x => x.Name == filter1)
                                          .First();
    
        PropertyInfo prop2 = typeof(School).GetProperties()
                                          .Where(x => x.Name == filter2)
                                          .First();                                   
        var grouping = from s in school
                       group s by new {filter1 = prop1.GetValue(s), filter2 = prop2.GetValue(s)} into gr
                       select new {
                        Filter1 = gr.Key.filter1,
                        Filter2 = gr.Key.filter2,
                        TotalGrades = gr.Sum(x => x.Grades),
                        Count = gr.Count(),
                        Average = gr.Average(x => x.Grades)
                       };
        grouping.Dump(); // Linq pad specific 
    }
    
    // Define other methods and classes here
    public class School{
        public string Student {get;set;}
        public string Teacher {get;set;}
        public string Subject {get;set;}
        public int Grades {get;set;}
    }
    

    Filter1 Filter2 TotalGrades Count Average

    A 老师数学 240 3 80

    B 老师历史 180 2 90

    C老师英语100 1 100

    B老师英语190 2 95


    Filter1 Filter2 TotalGrades Count Average

    数学老师A 240 3 80

    历史老师B 180 2 90

    英语教师 C 100 1 100

    英语老师B 190 2 95

    【讨论】:

      猜你喜欢
      • 2012-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      • 2013-03-12
      • 2012-05-23
      • 2013-08-11
      • 2013-09-14
      相关资源
      最近更新 更多