我有一个具有两个int属性的对象列表。该列表是另一个linq查询的输出。对象:
public class DimensionPair
{
public int Height { get; set; }
public int Width { get; set; }
}
我想在列表中找到并返回具有最大Height属性值的对象。
我可以设法获得高度值的最大值,但不是对象本身。
我可以用Linq做这个吗?如何?
我有一个具有两个int属性的对象列表。该列表是另一个linq查询的输出。对象:
public class DimensionPair
{
public int Height { get; set; }
public int Width { get; set; }
}
我想在列表中找到并返回具有最大Height属性值的对象。
我可以设法获得高度值的最大值,但不是对象本身。
我可以用Linq做这个吗?如何?
当前回答
你也可以通过重写扩展方法来升级Mehrdad Afshari的解决方案,使其更快(更好看):
static class EnumerableExtensions
{
public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
{
var enumerator = container.GetEnumerator();
if (!enumerator.MoveNext())
throw new ArgumentException("Container is empty!");
var maxElem = enumerator.Current;
var maxVal = valuingFoo(maxElem);
while (enumerator.MoveNext())
{
var currVal = valuingFoo(enumerator.Current);
if (currVal.CompareTo(maxVal) > 0)
{
maxVal = currVal;
maxElem = enumerator.Current;
}
}
return maxElem;
}
}
然后使用它:
var maxObject = list.MaxElement(item => item.Height);
这个名称对于使用c++的人来说是清楚的(因为这里有std::max_element)。
其他回答
你为什么不试试这个??:
var itemsMax = items.Where(x => x.Height == items.Max(y => y.Height));
或者更优化:
var itemMaxHeight = items.Max(y => y.Height);
var itemsMax = items.Where(x => x.Height == itemMaxHeight);
嗯?
先排序,然后再选择第一件商品是浪费大量时间。你不关心它们的顺序。
相反,您可以使用聚合函数来根据您正在寻找的内容选择最佳项目。
var maxHeight = dimensions
.Aggregate((agg, next) =>
next.Height > agg.Height ? next : agg);
var maxHeightAndWidth = dimensions
.Aggregate((agg, next) =>
next.Height >= agg.Height && next.Width >= agg.Width ? next: agg);
我们在MoreLINQ中有一个扩展方法可以做到这一点。你可以看看这里的实现,但基本上这是一个遍历数据的例子,记住我们迄今为止看到的最大元素以及它在投影下产生的最大值。
在你的情况下,你会这样做:
var item = items.MaxBy(x => x.Height);
这比这里提出的除Mehrdad的第二个解决方案(基本上与MaxBy相同)之外的任何解决方案都更好(IMO):
它是O(n)不像之前接受的答案,它在每次迭代中找到最大值(使它成为O(n²)) 排序解是O(n log n) 取Max值,然后找到具有该值的第一个元素是O(n),但在序列上迭代两次。在可能的情况下,应该以单遍方式使用LINQ。 它比聚合版本更容易阅读和理解,并且每个元素只计算一次投影
根据Cameron最初的回答,以下是我刚刚在SilverFlow库的FloatingWindowHost的增强版本中添加的内容(从http://clipflair.codeplex.com源代码中的FloatingWindowHost.cs复制)
public int MaxZIndex
{
get {
return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
int w = Canvas.GetZIndex(window);
return (w > maxZIndex) ? w : maxZIndex;
});
}
}
private void SetTopmost(UIElement element)
{
if (element == null)
throw new ArgumentNullException("element");
Canvas.SetZIndex(element, MaxZIndex + 1);
}
值得注意的是Canvas上面的代码。ZIndex是一个附加属性,可用于各种容器中的uielement,而不仅仅是在Canvas中托管时使用(参见在Silverlight中不使用Canvas控件控制渲染顺序(ZOrder))。我猜你甚至可以通过修改这段代码为UIElement创建一个SetTopmost和SetBottomMost静态扩展方法。
你也可以通过重写扩展方法来升级Mehrdad Afshari的解决方案,使其更快(更好看):
static class EnumerableExtensions
{
public static T MaxElement<T, R>(this IEnumerable<T> container, Func<T, R> valuingFoo) where R : IComparable
{
var enumerator = container.GetEnumerator();
if (!enumerator.MoveNext())
throw new ArgumentException("Container is empty!");
var maxElem = enumerator.Current;
var maxVal = valuingFoo(maxElem);
while (enumerator.MoveNext())
{
var currVal = valuingFoo(enumerator.Current);
if (currVal.CompareTo(maxVal) > 0)
{
maxVal = currVal;
maxElem = enumerator.Current;
}
}
return maxElem;
}
}
然后使用它:
var maxObject = list.MaxElement(item => item.Height);
这个名称对于使用c++的人来说是清楚的(因为这里有std::max_element)。