tags:

views:

53

answers:

2

is there a good way to do a Linq GroupBy where the grouping key is determined at runtime? e.g. I want the grouping key to be built from a user-selected list of fields - can you do this? I know I can do it easily if I convert everything to a table of strings, but I was wondering if there was an elegant or clever way to accomplish this otherwise.

class Item
{
    public int A, B;
    public DateTime D;
    public double X, Y, Z;
}

I have a List<Item> called data. I want to do things like retrieve the sum of X grouped by A, or the sums of X, Y, and Z, grouped by A and B. but what fields go into the grouping should be able to be specified at runtime in some way.

+4  A: 

Get the Dynamic LINQ code and use the extension from it that allows you to specify the keySelector with a string.

var query = db.Foo.GroupBy( "{0}", "GroupedProperty", groupKey );

You might also want to consider adding your own extension if you want to get back the entire object grouped by the key.

public static IQueryable GroupByComplete( this IQueryable source, string keySelector, params object[] values )
{
    if (source == null) throw new ArgumentNullException( "source" );
    if (keySelector == null) throw new ArgumentNullException( "keySelector" );
    LambdaExpression keyLambda = DynamicExpression.ParseLambda( source.ElementType, null, keySelector, values );
    return source.Provider.CreateQuery(
        Expression.Call(
            typeof( Queryable ), "GroupBy",
            new Type[] { source.ElementType, keyLambda.Body.Type },
            source.Expression, Expression.Quote( keyLambda ) ) );
}
tvanfosson
that looks like what I want. looking at the source code, I guess it's more complex than I imagined, good thing I didn't spend much time trying to write it myself :)
toasteroven
+3  A: 

All you need is to construct a Func<Item, TKey> at run-time:

var arg = Expression.Parameter(typeof(Item), "item");
var body = Expression.Property(arg, "D");
var lambda = Expression.Lambda<Func<Item, DateTime>>(body, arg);
var keySelector = lambda.Compile();

Usage:

var result = source.GroupBy(keySelector);

It gets slightly (but not much) more difficult if you don't know the type of the property at compile-time.

dtb
was trying to do that but couldn't figure out the syntax, thanks for the example.
toasteroven