5

I have a telerik grid that I databind a list of objects to on a page. The users can group columns to their liking when viewing the data. I am writing a function to export this data in an excel document and would like to preserve the grouping that the user has on the page. I am able to get the string names of the properties of my objects that the user has grouped by.

What I am stuck on is how to do the grouping during runtime. There are plenty of examples such as this: Group by with multiple columns using lambda describing how to group by multiple properties during compile time when you know ahead of time. I however, do not know what to group by until during run time.

Is there any suggestions on how to go about grouping during runtime?

Community
  • 1
  • 1
Justin
  • 6,373
  • 9
  • 46
  • 72
  • Linq does not provide methods for grouping by field names. You have to use expressions. However you can build an expression in runtime by field name. – neleus Nov 12 '14 at 14:35

3 Answers3

2

You're probably looking for something like the answer to this SO question, notably:

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.
    }
}    
Community
  • 1
  • 1
Corey Adler
  • 15,897
  • 18
  • 66
  • 80
0

I would do this with Dynamic Linq (http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library)

Example:

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);
            }
        }            
    }
}
aquinas
  • 23,318
  • 5
  • 58
  • 81
  • I am using your suggestion in my code and I am getting some behavior I do not understand. If I have: `systemList.GroupBy("new (" + String.Join(", ", groupFields) + ")", "it").Select("new(it.Key as Key, it as System)");` it will work fine, but if I replace it with sys like this: `systemList.GroupBy("new (" + String.Join(", ", groupFields) + ")", "sys").Select("new(sys.Key as Key, sys as System)");` I get a `No property or field 'sys' exists in type 'SystemSummaryData'` exception. How is changing it to sys causing that? – Justin Nov 13 '14 at 17:34
0

You're trying to group by the name of a particular property you have at run time. It's similar to this except it's fixed at 2 filters. This is just another option for you. use linq pad to test it out.

Are the number of filters fixed or is that dynamic as well?

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

Teacher A Math 240 3 80

Teacher B History 180 2 90

Teacher C English 100 1 100

Teacher B English 190 2 95


Filter1 Filter2 TotalGrades Count Average

Math Teacher A 240 3 80

History Teacher B 180 2 90

English Teacher C 100 1 100

English Teacher B 190 2 95

crackhaus
  • 1,176
  • 1
  • 15
  • 27