如何让 LINQ 返回具有给定属性最大值的对象? [复制]

Posted

技术标签:

【中文标题】如何让 LINQ 返回具有给定属性最大值的对象? [复制]【英文标题】:How can I get LINQ to return the object which has the max value for a given property? [duplicate] 【发布时间】:2010-07-06 17:33:11 【问题描述】:

如果我有一个看起来像这样的类:

public class Item

    public int ClientID  get; set; 
    public int ID  get; set; 

以及这些项目的集合...

List<Item> items = getItems();

如何使用 LINQ 返回具有最高 ID 的单个“项目”对象?

如果我这样做:

items.Select(i => i.ID).Max(); 

我只会得到最高的 ID,而我真正想要返回的是具有最高 ID 的 Item 对象本身?我希望它返回一个“Item”对象,而不是一个 int。

【问题讨论】:

除了这个页面上的答案之外,我认为这个答案也值得一提:***.com/a/1101979/4880924 谁能比 Jon Skeet 回答得更好?为他的回答提供推理。 【参考方案1】:

这只会循环一次。

Item biggest = items.Aggregate((i1,i2) => i1.ID > i2.ID ? i1 : i2);

谢谢尼克 - 这是证据

class Program

    static void Main(string[] args)
    
        IEnumerable<Item> items1 = new List<Item>()
        
            new Item() ClientID = 1, ID = 1,
            new Item() ClientID = 2, ID = 2,
            new Item() ClientID = 3, ID = 3,
            new Item() ClientID = 4, ID = 4,
        ;
        Item biggest1 = items1.Aggregate((i1, i2) => i1.ID > i2.ID ? i1 : i2);

        Console.WriteLine(biggest1.ID);
        Console.ReadKey();
    




public class Item

    public int ClientID  get; set; 
    public int ID  get; set; 
  

重新排列列表,得到相同的结果

【讨论】:

我赞成这个,因为我喜欢这个概念。我不知道这段代码是否真的会做被问到的事情。 我不止一次遇到过同样的问题,这是我找到的最优雅的解决方案。谢谢! 好吧,Item itemBig = items1.Count() &gt; 0 ? items1.Aggregate((i1, i2) =&gt; i1.ID &gt; i2.ID ? i1 : i2) : null; @ruffin 我会使用 items1.Any()。这样你就不会迭代整个集合。 这应该是公认的答案。它完美地工作,当您控制“聚合”的发生方式时,您可以简单地始终返回具有最高/最低测量值的实例,或者就此而言,您喜欢的任何其他比较。【参考方案2】:
.OrderByDescending(i=>i.id).First(1)

关于性能问题,这种方法很可能在理论上比线性方法慢。然而,实际上,大多数时候我们并没有处理大到足以产生任何影响的数据集。

如果性能是主要问题,Seattle Leonard 的回答应该会给您线性时间复杂度。或者,您也可以考虑从在恒定时间返回最大值项的不同数据结构开始。

【讨论】:

有效,但它是 nlogn 而不是线性时间。 tzaman:理论上,LINQ 系统可以识别“orderby().take()”模式并使用线性时间算法——但你是对的它可能不会。 .OrderByDescending(i=&gt;i.id).First() 将返回对象本身,而不是包含 1 项的枚举。 我知道这是超级旧的,但@stevecook .FirstOrDefault() 可能是一个更好的选择,因为它不太容易出现问题。 @Dortimer 如果这是一个有效错误,那么这与更安全相反。应该引起大家的注意。 “错误隐藏”是一个坏习惯(一种反模式)。当我期望一个元素时,我偶尔仍会使用 FirstOrDefault(),但它只是为了能够产生比默认值更多信息的异常消息【参考方案3】:
int max = items.Max(i => i.ID);
var item = items.First(x => x.ID == max);

这当然假设 items 集合中有元素。

【讨论】:

"Where" 返回所有具有最大值的项目,也许我们只想要第一个,在这种情况下 "First" 会是最好的 +1 为清楚起见;我在上面描述的 tzaman 营地(无论如何也可以尝试 morelinq...) 此代码返回具有最大 ID 的项目。正如 Jon Skeet 所说,Max 返回最大值,而不是包含最大值的项目。 立即可读,不需要新的库/扩展 这个答案做了不必要的工作。该列表在第一次调用Max 时被完全迭代。对First 的下一次调用将对列表执行另一次迭代以查找元素。【参考方案4】:

使用来自morelinq 项目的MaxBy

items.MaxBy(i => i.ID);

【讨论】:

@Reed:我想你现在已经知道为什么了……但是对于其他读者:Max 返回最大值,而不是包含最大值的项目。请注意,MaxBy 也在 Reactive Extensions 框架中的 System.Interactive 中。 @Jon:是的 - 我总是忘记那个 - 我一直在使用它,认为它也是正确的 无需编写自己的方法(或添加另一个依赖项);西雅图伦纳德和尼克拉森都给出了做同样事情的简单单线。 @BlueRaja: morelinq 有很多有用的功能,我几乎在每个项目中都使用了它。 :) 另外,我发现MaxBy 的意图比等效的Aggregate 语法要清晰得多——减少心理解析时间在以后总是有益的。当然,任何有功能经验的人都会很快认出折叠,但不是每个人都有。最后,NickLarsen 的解决方案进行了两次传递(如果有多个最大值,则会出现问题)。 @tzaman 如果有多个最大值,您的解决方案会遇到与 NickLarsen 相同的问题,不是吗?在某些时候,如果有多个具有最大值的代码,代码需要选择哪个是“正确的”。【参考方案5】:

这是源自@Seattle Leonard 回答的扩展方法:

 public static T GetMax<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
 
     return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
 

【讨论】:

【参考方案6】:

如果你不想使用MoreLINQ,又想获得线性时间,也可以使用Aggregate

var maxItem = 
  items.Aggregate(
    new  Max = Int32.MinValue, Item = (Item)null ,
    (state, el) => (el.ID > state.Max) 
      ? new  Max = el.ID, Item = el  : state).Item;

这会记住匿名类型中的当前最大元素 (Item) 和当前最大值 (Item)。然后你只需选择Item 属性。这确实有点难看,您可以将其包装到 MaxBy 扩展方法中以获得与 MoreLINQ 相同的东西:

public static T MaxBy(this IEnumerable<T> items, Func<T, int> f) 
  return items.Aggregate(
    new  Max = Int32.MinValue, Item = default(T) ,
    (state, el) => 
      var current = f(el.ID);
      if (current > state.Max) 
        return new  Max = current, Item = el ;
      else 
        return state; 
    ).Item;

【讨论】:

【参考方案7】:

或者你可以编写自己的扩展方法:

static partial class Extensions

    public static T WhereMax<T, U>(this IEnumerable<T> items, Func<T, U> selector)
    
        if (!items.Any())
        
            throw new InvalidOperationException("Empty input sequence");
        

        var comparer = Comparer<U>.Default;
        T   maxItem  = items.First();
        U   maxValue = selector(maxItem);

        foreach (T item in items.Skip(1))
        
            // Get the value of the item and compare it to the current max.
            U value = selector(item);
            if (comparer.Compare(value, maxValue) > 0)
            
                maxValue = value;
                maxItem  = item;
            
        

        return maxItem;
    

【讨论】:

【参考方案8】:

试试这个:

var maxid = from i in items
            group i by i.clientid int g
            select new  id = g.Max(i=>i.ID 

【讨论】:

【参考方案9】:

在 LINQ 中,您可以通过以下方式解决它:

Item itemMax = (from i in items
     let maxId = items.Max(m => m.ID)
     where i.ID == maxId
     select i).FirstOrDefault();

【讨论】:

【参考方案10】:

您可以使用捕获的变量。

Item result = items.FirstOrDefault();
items.ForEach(x =>

  if(result.ID < x.ID)
    result = x;
);

【讨论】:

以上是关于如何让 LINQ 返回具有给定属性最大值的对象? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 LINQ 选择具有最小或最大属性值的对象

如何使用 LINQ 从对象列表中获取唯一的属性列表?

使用 linq 返回对象的特定属性列表

如何选择具有最低属性 linq 查询语法的对象

Linq 查询以返回具有特定属性值的嵌套数组

如何使用 linq 查询具有嵌套列表的对象?