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

C# 如何对集合中所有对象的属性执行.Max(),并返回具有最大值的对象,c#,linq,object,max,C#,Linq,Object,Max,我有一个具有两个int属性的对象列表。该列表是另一个linq查询的输出。对象: public class DimensionPair { public int Height { get; set; } public int Width { get; set; } } 我想在列表中找到并返回属性值最大的对象 我可以设法获得高度值的最高值,但不能获得对象本身 我能和Linq一起做这个吗?如何进行?这需要排序(O(n logn)),但非常简单和灵活。另一个优势是能够将其与LINQ

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

public class DimensionPair  
{
    public int Height { get; set; }
    public int Width { get; set; }
}
我想在列表中找到并返回属性值最大的对象

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

我能和Linq一起做这个吗?如何进行?

这需要排序(O(n logn)),但非常简单和灵活。另一个优势是能够将其与LINQ to SQL结合使用:

var maxObject = list.OrderByDescending(item => item.Height).First();
注意,这样做的优点是只枚举一次
列表
序列。虽然如果
list
是一个同时不变的
list
,这可能无关紧要,但对于任意
IEnumerable
对象来说,这可能很重要。没有任何东西可以保证序列不会在不同的枚举中更改,因此多次执行该操作的方法可能是危险的(并且效率低下,这取决于序列的性质)。然而,对于大序列来说,这仍然不是一个理想的解决方案。如果您有大量的项目可以一次完成,而无需排序和其他任何东西,我建议您手动编写自己的
MaxObject
扩展(O(n)):


我相信按你想得到的最大值的列排序,然后抓住第一个应该是可行的。但是,如果有多个具有相同最大值的对象,则只会抓取一个:

private void Test()
{
    test v1 = new test();
    v1.Id = 12;

    test v2 = new test();
    v2.Id = 12;

    test v3 = new test();
    v3.Id = 12;

    List<test> arr = new List<test>();
    arr.Add(v1);
    arr.Add(v2);
    arr.Add(v3);

    test max = arr.OrderByDescending(t => t.Id).First();
}

class test
{
    public int Id { get; set; }
}
private void Test()
{
测试v1=新测试();
v1.Id=12;
测试v2=新测试();
v2.Id=12;
测试v3=新测试();
v3.Id=12;
List arr=新列表();
arr.Add(v1);
arr.Add(v2);
arr.Add(v3);
testmax=arr.OrderByDescending(t=>t.Id).First();
}
课堂测试
{
公共int Id{get;set;}
}

先下单,然后再选择第一个项目,这会浪费大量时间在第一个项目之后再下单。你不在乎这些东西的顺序

相反,您可以使用聚合函数根据所需内容选择最佳项目

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);
我们有足够的时间来做这件事。您可以查看那里的实现,但基本上它是一个遍历数据的案例,记住我们迄今为止看到的最大元素以及它在投影下产生的最大值

在您的情况下,您可以执行以下操作:

var item = items.MaxBy(x => x.Height);
这比这里介绍的任何解决方案都好(IMO),而不是Mehrdad的第二个解决方案(基本上与
MaxBy
)相同):

  • 它是O(n),不同于在每次迭代中找到最大值(使其为O(n^2))
  • 排序解决方案是O(n logn)
  • Max
    值,然后找到具有该值的第一个元素是O(n),但在序列上迭代两次。在可能的情况下,应该以单通道方式使用LINQ
  • 与聚合版本相比,它的阅读和理解要简单得多,并且每个元素只计算一次投影

    • 你为什么不试试这个??:

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

      在NHibernate中(使用NHibernate.Linq),您可以按如下方式执行:

      return session.Query<T>()
                    .Single(a => a.Filter == filter &&
                                 a.Id == session.Query<T>()
                                                .Where(a2 => a2.Filter == filter)
                                                .Max(a2 => a2.Id));
      

      这对我来说似乎非常有效。

      根据Cameron的初步回答,以下是我刚刚在增强版SilverFlow库的FloatingWindowHost中添加的内容(从源代码处的FloatingWindowHost.cs复制)


      值得注意的是,关于上面的代码,Canvas.ZIndex是一个附加属性,可用于各种容器中的UIElements,而不仅仅是在画布中托管时使用(请参阅)。通过修改此代码,您甚至可以轻松地为UIElement创建SetTopmost和SetBottomMost静态扩展方法。

      您还可以通过将扩展方法重写为更快(更好看)的方法来升级Mehrdad Afshari的解决方案:


      使用C++的人将清楚这个名字(因为在那里有STD::Max元素)。但我认为需要一个具有以下限制的解决方案:

    • 朴素、简洁的林克
    • O(n)复杂性
    • 每个元素对属性的求值不要超过一次
    • 这是:

      public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
          return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
              .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
      }
      
      public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
          return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
              .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
      }
      
      public static T MaxBy(此IEnumerable en,Func evaluate),其中R:IComparable{
      返回en.Select(t=>newtuple(t,evaluate(t)))
      .Aggregate((max,next)=>next.Item2.CompareTo(max.Item2)>0?next:max.Item1;
      }
      公共静态T MinBy(此IEnumerable en,Func evaluate),其中R:i可比较{
      返回en.Select(t=>newtuple(t,evaluate(t)))
      .Aggregate((最大,下一个)=>next.Item2.CompareTo(最大项2)<0?下一个:最大)。Item1;
      }
      
      用法:

      IEnumerable<Tuple<string, int>> list = new[] {
          new Tuple<string, int>("other", 2),
          new Tuple<string, int>("max", 4),
          new Tuple<string, int>("min", 1),
          new Tuple<string, int>("other", 3),
      };
      Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
      Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4
      
      IEnumerable list=new[]{
      新元组(“其他”,2),
      新元组(“max”,4),
      新元组(“min”,1),
      新元组(“其他”,3),
      };
      Tuple min=list.MinBy(x=>x.Item2);//“min”,1
      Tuple max=list.MaxBy(x=>x.Item2);//“max”,4
      
      Rats,在我发布之前没有看到这一点。这基本上就是我们在MoreLINQ中所做的,除了我们直接使用GetEnumerator进行迭代,而不是使用“first”标志。我认为这使代码更简单一些。我们还允许使用Comparer并允许传入一个,而不是要求U实现IComparable,但这是一个次要问题。最后一部分是一个简单问题的许多不必要的复杂性。这是一个更一般问题的解决方案。基本上,您可以声明这样一种扩展方法,以便在需要时将复杂性抽象掉。这难道不是软件开发的工作方式吗?为了完整性,有没有理由选择
      OrderByDescending().First()
      OrderBy().Last()
      ?我在代码中使用第二个选项(至少更短),因为我认为两者的成本相同?(编辑:好吧,没关系…)因为这是O(n^2)。对于列表中的每个项目,它将遍历所有项目,并找到高度最大的项目,然后检查当前项目是否存在
      select *
      from TableName foo
      where foo.Filter = 'Filter On String'
      and foo.Id = (select cast(max(bar.RowVersion) as INT)
                    from TableName bar
                    where bar.Name = 'Filter On String')
      
          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);
          }
      
      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);
      
      public static T MaxBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
          return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
              .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) > 0 ? next : max).Item1;
      }
      
      public static T MinBy<T, R>(this IEnumerable<T> en, Func<T, R> evaluate) where R : IComparable<R> {
          return en.Select(t => new Tuple<T, R>(t, evaluate(t)))
              .Aggregate((max, next) => next.Item2.CompareTo(max.Item2) < 0 ? next : max).Item1;
      }
      
      IEnumerable<Tuple<string, int>> list = new[] {
          new Tuple<string, int>("other", 2),
          new Tuple<string, int>("max", 4),
          new Tuple<string, int>("min", 1),
          new Tuple<string, int>("other", 3),
      };
      Tuple<string, int> min = list.MinBy(x => x.Item2); // "min", 1
      Tuple<string, int> max = list.MaxBy(x => x.Item2); // "max", 4