2016-02-17 12 views
7

の性質を持つ要素を見つけることは何ですか?たぶん私はこれのような何かをする必要がありますか?は、私がこの</p> <pre><code>var itemWithMaxPropValue = collection.OrderByDescending(x => x.Property).First(); </code></pre> <p>好きしかし、それはパフォーマンスの観点からは良い方法である最大値のプロパティを持つ要素を見つけるために、より速く一般最大値

var maxValOfProperty = collection.Max(x => x.Property); 
var itemWithMaxPropValue = collection 
           .Where(x => x.Property == maxValueOfProperty).First(); 
+1

を置くことができる私は 'に行きますマックス 'それは特別に設計されて以来...最大値を見つけるための並べ替えはあまりにも多いようです...また、私は最大値を見つけるために 'Where'を使用しませんが、' Single' – Ian

+0

'OrderByDescending'/QuicksortはO(n^2)と' Max' O(n)と 'Where' O(n) - したがって、2番目のapprochはもっと速くなるはずです – fubo

+0

https://www.nuget.org/packages/MoreLinq.Source.MoreEnumerable.MaxBy/ in-memory use(doesn –

答えて

4

どちらの解決法もあまり効率的ではありません。最初の解決策は、全体のコレクションをソートすることです。 2つ目の解決策では、コレクションを2回トラバースする必要があります。しかし、コレクションをソートすることなく、最大のプロパティ値を持つアイテムを一度に見つけることができます。ライブラリMoreLINQにはMaxBy拡張機能があります。それとも、同じ機能を実装することができます

public static TSource MaxBy<TSource, TProperty>(this IEnumerable<TSource> source, 
    Func<TSource, TProperty> selector) 
{ 
    // check args   

    using (var iterator = source.GetEnumerator()) 
    { 
     if (!iterator.MoveNext())    
      throw new InvalidOperationException(); 

     var max = iterator.Current; 
     var maxValue = selector(max); 
     var comparer = Comparer<TProperty>.Default; 

     while (iterator.MoveNext()) 
     { 
      var current = iterator.Current; 
      var currentValue = selector(current); 

      if (comparer.Compare(currentValue, maxValue) > 0) 
      { 
       max = current; 
       maxValue = currentValue; 
      } 
     } 

     return max; 
    } 
} 

使い方は簡単です:マックスはNだけ時間の複雑さを持っているので、Max速くなりながら

var itemWithMaxPropValue = collection.MaxBy(x => x.Property); 
4

それが具体的にその目的のために設計されているので、私はMaxで行きます。 Max値をソートするのはあまりにも多いようです。

また、最大値を見つけるのにWhereを使用しませんが、Singleはここで必要なのはSingleです。使用

var maxValOfProperty = collection.Max(x => x.Property); 
var itemWithMaxPropValue = collection 
          .Single(x => x.Property == maxValueOfProperty); 

または代わりFirstKathiにより示唆されるように)MoreLINQを使用して

var maxValOfProperty = collection.Max(x => x.Property); 
var itemWithMaxPropValue = collection 
          .First(x => x.Property == maxValueOfProperty); 

あるいは、(コレクションは最大値の重複が含まれている場合)、あなたがそれを行うことができMaxByと:

var itemWithMaxPropValue = collection.MaxBy(x => x.Property); 

このチェックをpost、回答時はJon Skeetとしてください。

+1

コレクションに複数のアイテムが含まれている(プロパティの最大値を持つ)場合、OPはどちらを取るべきかを気にしない場合は、 'Single'の代わりに' First'を使いたいかもしれませんこの場合。 –

+0

@ YacoubMassadあなたが正しいです、それは確かに有効な選択肢です。 – Ian

+0

2つの行を結合すると 'collection.Max(x => x.Property)'が複数回評価されることにも注意してください。 –

0

特定の機能の下にある最大要素は、次の2つの機能によっても検出できます。

static class Tools 
{ 
    public static T ArgMax<T, R>(T t1, T t2, Func<T, R> f) 
    where R : IComparable<R> 
    { 
     return f(t1).CompareTo(f(t2)) > 0 ? t1 : t2; 
    } 

    public static T ArgMax<T, R>(this IEnumerable<T> Seq, Func<T, R> f) 
    where R : IComparable<R> 
    { 
     return Seq.Aggregate((t1, t2) => ArgMax<T, R>(t1, t2, f)); 
    } 
} 

上記の解決策は次のとおりです。 ArgMaxの最初のオーバーロードは、比較可能性を実装する型にTの両方のインスタンスをマップする引数としてコンパレータをとります。これらの最大値が返されます。 2番目のオーバーロードは、引数としてシーケンスを取り、単純に最初の関数を集約します。これは私が知っている最大限の検索のための最も一般的な、フレームワークの再利用と構造的に健全な形式です。最小値の探索は、第1の関数の比較を変更することによって同様に実施することができる。

5

ソートはN * log (N)です。あなたが探しているのは、例えば、LINQのが提供していないので、私はそれを実装する提案ArgMax機能である:

public static class EnumerableExtensions { 
    public static T ArgMax<T, K>(this IEnumerable<T> source, 
           Func<T, K> map, 
           IComparer<K> comparer = null) { 
     if (Object.ReferenceEquals(null, source)) 
     throw new ArgumentNullException("source"); 
     else if (Object.ReferenceEquals(null, map)) 
     throw new ArgumentNullException("map"); 

     T result = default(T); 
     K maxKey = default(K); 
     Boolean first = true; 

     if (null == comparer) 
     comparer = Comparer<K>.Default; 

     foreach (var item in source) { 
     K key = map(item); 

     if (first || comparer.Compare(key, maxKey) > 0) { 
      first = false; 
      maxKey = key; 
      result = item; 
     } 
     } 

     if (!first) 
     return result; 
     else 
     throw new ArgumentException("Can't compute ArgMax on empty sequence.", "source"); 
    } 
    } 

だから、あなたは、単に

var itemWithMaxPropValue = collection 
    .ArgMax(x => x.Property);