Select the maximal item from collection, by some criterion

假装没事ソ 提交于 2019-12-08 03:08:08

问题


i am new to .net 3.5. I have a collection of items:

IList<Model> models;

where

class Model
{
    public string Name
    {
       get;
       private set;
    }
}

I would like to get the element, which has the longest name's length. I tried

string maxItem = models.Max<Model>(model => model.Name.Length);

but it of course returns the maximum length (and I need a Model object). I know there is a way of doing this using the extension methods but I don't know how.


回答1:


This is how I got it to work. Maybe there's a better way, I'm not sure:

    decimal de = d.Max(p => p.Name.Length);
    Model a = d.First(p => p.Name.Length == de);



回答2:


There isn't a built-in way of doing this, unfortunately - but it's really easy to write an extension method to do it.

It was in one of my very first blog posts, in fact... note that there's a better implementation in one of the comments. I'll move it into the body if I get time.

EDIT: Okay, I have a slightly abbreviated version - it just returns the maximal element, using the given selector. No need to do a projection as well - do that once afterwards if you need to. Note that you could remove the constraint on TValue and use Comparer<TValue>.Default instead, or have an overload which allows the comparison to be specified as another parameter.

public static TSource MaxBy<TSource, TValue>(this IEnumerable<TSource> source,
                                             Func<TSource, TValue> selector)
    where TValue : IComparable<TValue>
{
    TValue maxValue = default(TValue);
    TSource maxElement = default(TSource);
    bool gotAny = false;

    foreach (TSource sourceValue in source)
    {
        TValue value = selector(sourceValue);
        if (!gotAny || value.CompareTo(maxValue) > 0)
        {
            maxValue = value;
            maxElement = sourceValue;
            gotAny = true;
        }
    }
    if (!gotAny)
    {
        throw new InvalidOperationException("source is empty");
    }
    return maxElement;
}

Sample use: (note type inference):

string maxName = models.MaxBy(model => model.Name.Length).Name;



回答3:


Here's another way of doing it. There's a version of Max that takes no criterion, and uses IComparable. So we could provide a way to wrap anything in a comparable object, with a delegate providing the comparison.

public class Comparable<T> : IComparable<Comparable<T>>
{
    private readonly T _value;
    private readonly Func<T, T, int> _compare;

    public Comparable(T v, Func<T, T, int> compare)
    {
        _value = v;
        _compare = compare;
    }

    public T Value { get { return _value; } }

    public int CompareTo(Comparable<T> other)
    {
        return _compare(_value, other._value);
    }
}

Then we can say:

Model maxModel = models.Select(m => new Comparable<Model>(m, (a, b) => a.Name.Length - b.Name.Length)).Max().Value;

This involves a lot of extra allocation, but it's sort of academically interesting (I think).




回答4:


You can use Aggregate. It can be done without writing new extension method.

models.Aggregate(
                new KeyValuePair<Model, int>(),
                (a, b) => (a.Value < b.Name.Length) ? new KeyValuePair<Model, int>(b, b.Name.Length) : a,
                a => a.Key);



回答5:


Is there anything gained by using the extension methods?

Perhaps a method or procedure with a simple iteration of the list would suffice?

Something to the effect of

Dim result as string = models(0).Name
for each m as Model in models
  if m.Name.length > result.length then
    result = m.Name
  end if
next



回答6:


Another way might be:

var item = (from m in models select m orderby m.Name.Length descending).FirstOrDefault(); 

First one will be the one with the longest length.



来源:https://stackoverflow.com/questions/384633/select-the-maximal-item-from-collection-by-some-criterion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!