2011-08-18 7 views
5

मेरे एप्लिकेशन को अक्सर तालिका को समूहबद्ध करने की आवश्यकता होती है, फिर पंक्ति को उस समूह के अधिकतम मूल्य के साथ वापस कर दें। LINQ में यह करना बहुत आसान है:LINQ - प्रत्येक समूह के लिए अधिकतम मान के साथ पंक्ति प्राप्त करने के लिए एक एक्सटेंशन विधि लिखना

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

अब मान लीजिए कि मैं इसे अपनी विधि में जोड़ना चाहता हूं। मैं इस लेखन की कोशिश की:

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); 
} 

दुर्भाग्य से यह संकलन नहीं करता है: maxKeySelector एक अभिव्यक्ति (ताकि आप r पर यह कॉल नहीं कर सकते है, और तुम भी अधिकतम करने के लिए इसे पारित नहीं कर सकते हैं तो मैं फिर से लिखने की कोशिश की,। maxKeySelector एक समारोह के बजाय एक अभिव्यक्ति बनाने:।

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); 
} 

अब इस संकलित लेकिन यह कार्यावधि में विफल रहता है: मैं की जरूरत है: "। असमर्थित क्वेरी ऑपरेटर 'मैक्स' के लिए इस्तेमाल किया अधिभार" यह है कि मैं क्या कर रहा हूँ अटक पर है MaxKeySlector को अधिकतम()

कोई सुझाव देने के लिए सही तरीका ढूंढें? मैं हम हूं LINQ से SQL में, जो एक फर्क पड़ता है।

उत्तर

4

सबसे पहले, मैं करते रहे कि तुम क्या करने की कोशिश कर रहे हैं यहां तक ​​कि आसान है चाहते हैं उससे अधिक आप 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 रों लेने के अलावा वास्तविक भाव ले जा सकते हैं का एक नया सेट को खोलने के लिए है। यह सबसे मानक LINQ प्रदाताओं के साथ संगत होना चाहिए।

+0

अच्छा, यह निश्चित रूप से अधिक संक्षिप्त है, लेकिन दुर्भाग्य से यह काम नहीं करता है। मुझे पहले की तरह एक ही त्रुटि मिलती है: "असमर्थित अधिभार क्वेरी ऑपरेटर 'ऑर्डरबी' के लिए प्रयोग किया जाता है"। – ctkrohn

+0

@ctkrohn: आप किस LINQ प्रदाता का उपयोग कर रहे हैं? यह LINQ से Entities .... और LINQ से ऑब्जेक्ट्स पर मेरे लिए ठीक काम कर रहा है। – StriplingWarrior

+0

यह एक LINQ से SQL है। – ctkrohn

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); 

बेस्ट ऑफ लक!

+0

यह चालाक और सहायक दिखती है, लेकिन शायद मेरे उद्देश्यों के लिए अधिक है। यदि मैं किसी भी अन्य तरीकों को कारक बनाना चाहता हूं, तो मैं इस तकनीक को बुकमार्क कर दूंगा, लेकिन अभी के लिए कोड में कोड लिखना शायद आसान है। आपके प्रश्न में ऐसी विधि मांगने के लिए – ctkrohn

+0

@ctkrohn, welp, आप पर शर्म की बात है। अगर मैं चुन रहा था, तो मैंने इसे भी इनलाइन लिखा होगा। लेकिन मुझे आपके मूल प्रश्न के बारे में एक और अधिक सुंदर जवाब नहीं दिख रहा है। मुझे लगता है, मुझे लगता है। कोड लिखने के लिए समय निकालने के लिए –

+0

+1 जो मैं प्रदान करने के लिए बहुत आलसी था। – StriplingWarrior

संबंधित मुद्दे