当前位置:  开发笔记 > 编程语言 > 正文

LINQ:如何对集合中所有对象的属性执行.Max()并返回具有最大值的对象

如何解决《LINQ:如何对集合中所有对象的属性执行.Max()并返回具有最大值的对象》经验,为你挑选了5个好方法。

我有一个具有两个int属性的对象列表.该列表是另一个linq查询的输出.物体:

public class DimensionPair  
{
    public int Height { get; set; }
    public int Width { get; set; }
}

我想找到并返回列表中具有最大Height属性值的对象.

我可以设法获得值的最高值,Height但不能获得对象本身.

我可以用Linq做到这一点吗?怎么样?



1> Jon Skeet..:

我们有一个扩展方法,可以在MoreLINQ中完成此操作.您可以查看那里的实现,但基本上是迭代数据的情况,记住我们到目前为止看到的最大元素以及它在投影下产生的最大值.

在你的情况下你会做类似的事情:

var item = items.MaxBy(x => x.Height);

除了Mehrdad的第二个解决方案(基本相同)之外,这比其他任何解决方案更好(IMO MaxBy):

它是O(n)不同于先前接受的答案,它在每次迭代中找到最大值(使其为O(n ^ 2))

订购解决方案是O(n log n)

Max的值,然后找出与该值的第一个元素是O(n),但在序列迭代两次.在可能的情况下,您应该以单通方式使用LINQ.

阅读和理解比聚合版本简单得多,并且每个元素只评估一次投影


@sgissinger:你错过了这一点 - 这将给出集合中的最大高度,而不是具有*最大高度的项目*.
Linq扩展已经有一个[`Max`](http://msdn.microsoft.com/en-us/library/Bb343284(v = vs.90).aspx)方法,你也可以使用lambda表达式`var item = items.Max(x => x.Height);`
FWIW这是聚合版本:`items.Aggregate((i,j)=> i.Height> j .Height?i:j)`
@orad:是的,那样做 - 但表达力要低得多,IMO.

2> Mehrdad Afsh..:

这需要排序(O(n log n)),但非常简单和灵活.另一个优点是能够将它与LINQ to SQL一起使用:

var maxObject = list.OrderByDescending(item => item.Height).First();

请注意,这具有list仅枚举序列一次的优点.虽然在此期间如果不改变可能无关紧要,但它对任意对象list都很List重要IEnumerable.没有什么能保证序列在不同的枚举中不会改变,因此多次执行它的方法可能是危险的(并且效率低,取决于序列的性质).然而,对于大型序列来说,它仍然不是理想的解决方案.我建议MaxObject手动编写自己的扩展,如果你有一大堆项目可以在一次通过中完成,而无需排序和其他任何东西(O(n)):

static class EnumerableExtensions {
    public static T MaxObject(this IEnumerable source, Func selector)
      where U : IComparable {
       if (source == null) throw new ArgumentNullException("source");
       bool first = true;
       T maxObj = default(T);
       U maxKey = default(U);
       foreach (var item in source) {
           if (first) {
                maxObj = item;
                maxKey = selector(maxObj);
                first = false;
           } else {
                U currentKey = selector(item);
                if (currentKey.CompareTo(maxKey) > 0) {
                    maxKey = currentKey;
                    maxObj = item;
                }
           }
       }
       if (first) throw new InvalidOperationException("Sequence is empty.");
       return maxObj;
    }
}

并使用它:

var maxObject = list.MaxObject(item => item.Height);


这是一个更通用的问题的解决方案.基本上,您可以声明这样的扩展方法,以便在需要时抽象出*复杂性.
这不是软件开发的工作原理吗?
老鼠,在我发布之前没看到这个.这基本上就是我们在MoreLINQ中所做的,除了我们直接使用GetEnumerator进行迭代而不是使用"first"标志.我认为它使代码更简单.我们还允许使用Comparer 并允许传入,而不是要求U实现IComparable,但这是一个副作用.

3> Cameron MacF..:

进行订购然后选择第一项是浪费了大量时间在第一项之后订购商品.你不关心那些的顺序.

相反,您可以使用聚合函数根据您要查找的内容选择最佳项目.

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


这应该是公认的答案.任何其他方法将迭代对象超过1次或使用不需要的库.另见:http://stackoverflow.com/questions/3188693/how-can-i-get-linq-to-return-the-object-which-has-the-max-value-for-a-given-prop/3188804#3188804

4> Dragouf..:

你为什么不尝试这个?:

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

嗯?


因为这是O(n ^ 2).对于列表中的每个项目,它遍历所有项目并找到具有最大高度的项目,然后检查当前项目是否具有相同的高度,并返回它.适用于小型列表,但超过100个项目您会注意到不同之处.
你确定items.Max(y => y.Height)的结果不会被缓存吗?我还想(就大O符号而言)O(2n)与O(n)相同?
你是对的,所以你必须在从lambda表达式中提取items.Max(y => y.Height)并将其保存在变量中之前.var itemMaxHeight = items.Max(y => y.Height); var item = items.Where(x => x.Height == itemMaxHeight); 我写得很快,因为我认为这可能是一种更简单的方法.但为了优化它,最好这样做,这是真的.

5> meustrus..:

到目前为止答案很棒!但我认为需要一个具有以下约束的解决方案:

    简洁明了的LINQ;

    O(n)复杂性;

    不要每个元素多次评估该属性.

这里是:

public static T MaxBy(this IEnumerable en, Func evaluate) where R : IComparable {
    return en.Select(t => new Tuple(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
}

public static T MinBy(this IEnumerable en, Func evaluate) where R : IComparable {
    return en.Select(t => new Tuple(t, evaluate(t)))
        .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
}

用法:

IEnumerable> list = new[] {
    new Tuple("other", 2),
    new Tuple("max", 4),
    new Tuple("min", 1),
    new Tuple("other", 3),
};
Tuple min = list.MinBy(x => x.Item2); // "min", 1
Tuple max = list.MaxBy(x => x.Item2); // "max", 4

推荐阅读
mobiledu2402852413
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有