如何让 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() > 0 ? items1.Aggregate((i1, i2) => i1.ID > i2.ID ? i1 : i2) : null;
@ruffin 我会使用 items1.Any()。这样你就不会迭代整个集合。
这应该是公认的答案。它完美地工作,当您控制“聚合”的发生方式时,您可以简单地始终返回具有最高/最低测量值的实例,或者就此而言,您喜欢的任何其他比较。【参考方案2】:
.OrderByDescending(i=>i.id).First(1)
关于性能问题,这种方法很可能在理论上比线性方法慢。然而,实际上,大多数时候我们并没有处理大到足以产生任何影响的数据集。
如果性能是主要问题,Seattle Leonard 的回答应该会给您线性时间复杂度。或者,您也可以考虑从在恒定时间返回最大值项的不同数据结构开始。
【讨论】:
有效,但它是 nlogn 而不是线性时间。 tzaman:理论上,LINQ 系统可以识别“orderby().take()”模式并使用线性时间算法——但你是对的它可能不会。.OrderByDescending(i=>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 返回具有给定属性最大值的对象? [复制]的主要内容,如果未能解决你的问题,请参考以下文章