LINQ - Writing an extension method to get the row with the maximum value for each group

My application often needs to group a table and then return a row with the maximum value for that group. This is pretty easy to do in LINQ:

myTable.GroupBy(r => r.FieldToGroupBy)
.Select(r => r.Max(s => s.FieldToMaximize))
.Join(
    myTable,
    r => r,
    r => r.FieldToMaximize,
    (o, i) => i)

Now suppose I want to distract this with my own method. I tried to write this:

public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TGroupKey>> groupKeySelector,
    Expression<Func<TSource, TMaxKey>> maxKeySelector)
    where TMaxKey : IComparable
{
    return source
        .GroupBy(groupKeySelector)
        .Join(
            source,
            g => g.Max(maxKeySelector),
            r => maxKeySelector(r),
                (o, i) => i);
}

Unfortunately, this is not compiled: maxKeySelector is an expression (therefore, you cannot call it on r, and you cannot even pass it to Max. Therefore, I tried to rewrite it by making maxKeySelector a function, not an expression:

public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TGroupKey>> groupKeySelector,
    Func<TSource, TMaxKey> maxKeySelector)
    where TMaxKey : IComparable
{
    return source
        .GroupBy(groupKeySelector)
        .Join(
            source,
            g => g.Max(maxKeySelector),
            r => maxKeySelector(r),
                (o, i) => i);
}

. : " , " "." , : maxKeySelector Max().

? LINQ to SQL, , , .

+5
2

, , , , , LINQ:

myTable.GroupBy(r => r.FieldToGroupBy)
    .Select(g => g.OrderByDescending(r => r.FieldToMaximize).FirstOrDefault())

... :

public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TGroupKey>> groupKeySelector,
    Expression<Func<TSource, TMaxKey>> maxKeySelector)
    where TMaxKey : IComparable
{
    return source
        .GroupBy(groupKeySelector)
        .Select(g => g.AsQueryable().OrderBy(maxKeySelector).FirstOrDefault());
}

, , IQueryable, LINQ, , Func s. LINQ.

+4

. "" , , (). , :

public static IQueryable<Item> _GroupMaxs(this IQueryable<Item> list)
{
    return list.GroupBy(x => x.Family)
        .Select(g => g.OrderByDescending(x => x.Value).First());
}

:

public static IQueryable<T> _GroupMaxs<T, TGroupCol, TValueCol>
    (this IQueryable<T> list, string groupColName, string valueColName)
{
    // (x => x.groupColName)
    var _GroupByPropInfo = typeof(T).GetProperty(groupColName);
    var _GroupByParameter = Expression.Parameter(typeof(T), "x");
    var _GroupByProperty = Expression
            .Property(_GroupByParameter, _GroupByPropInfo);
    var _GroupByLambda = Expression.Lambda<Func<T, TGroupCol>>
        (_GroupByProperty, new ParameterExpression[] { _GroupByParameter });

    // (x => x.valueColName)
    var _SelectParameter = Expression.Parameter(typeof(T), "x");
    var _SelectProperty = Expression
            .Property(_SelectParameter, valueColName);
    var _SelectLambda = Expression.Lambda<Func<T, TValueCol>>
        (_SelectProperty, new ParameterExpression[] { _SelectParameter });

    // return list.GroupBy(x => x.groupColName)
    //   .Select(g => g.OrderByDescending(x => x.valueColName).First());
    return list.GroupBy(_GroupByLambda)
        .Select(g => g.OrderByDescending(_SelectLambda.Compile()).First());
}

, . , . .

:

public class Item
{
    public string Family { get; set; }
    public int Value { get; set; }
}

foreach (Item item in _List
        .AsQueryable()._GroupMaxs<Item, String, int>("Family", "Value"))
    Console.WriteLine("{0}:{1}", item.Family, item.Value);

!

+4

All Articles