Reputation: 15355
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.
Upvotes: 2
Views: 290
Reputation: 3644
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
Upvotes: 0
Reputation: 5402
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);
Upvotes: 0
Reputation: 1502526
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;
Upvotes: 7
Reputation: 116714
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).
Upvotes: 1
Reputation: 101
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.
Upvotes: -1
Reputation: 103760
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);
Upvotes: 0