What is faster in finding element with property of maximum value

前端 未结 4 736
没有蜡笔的小新
没有蜡笔的小新 2021-01-18 02:31

Commonly, to find element with property of max value I do like this

var itemWithMaxPropValue = collection.OrderByDescending(x => x.Property).First();


        
相关标签:
4条回答
  • 2021-01-18 02:46

    The maximum element under some specified function can also be found by means of the following two functions.

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

    The solution above works as follows; the first overload of ArgMax takes a comparator as an argument which maps both instances of T to a type which implements comparability; a maximum of these is returned. The second overload takes a sequence as an argument and simply aggregates the first function. This is the most generic, framework-reusing and structurally sound formulation for maximum search I am aware of; searching the minimum can be implemented in the same way by changing the comparison in the first function.

    0 讨论(0)
  • 2021-01-18 02:51

    Both solutions are not very efficient. First solution involves sorting whole collection. Second solution requires traversing collection two times. But you can find item with max property value in one go without sorting collection. There is MaxBy extension in MoreLINQ library. Or you can implement same functionality:

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

    Usage is simple:

    var itemWithMaxPropValue = collection.MaxBy(x => x.Property); 
    
    0 讨论(0)
  • 2021-01-18 02:55

    Sorting is N * log (N) while Max has N only time complexity, so Max is faster. What you're looking for is ArgMax function which Linq doesn't provide, so I suggest implementing it, e.g:

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

    So you can put it simply

      var itemWithMaxPropValue = collection
        .ArgMax(x => x.Property);
    
    0 讨论(0)
  • 2021-01-18 02:58

    I will go with Max since it is specifically designed for that purpose. Sorting to find Max value seems to be too much.

    Also, I wouldn't use Where for finding the max, but Single - since what we need here is but a Single value.

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

    Or alternatively using First (if the collection contains duplicates of max value)

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

    Or, using MoreLINQ (as suggested by Kathi), you could do it with MaxBy:

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

    Check this post, on answer by Jon Skeet.

    0 讨论(0)
提交回复
热议问题