在不使用IComparable 的情况下查找Max / Min元素

说我有以下内容:

public Class BooClass { public int field1; public double field2; public DateTime field3; } public List booList; 

那么例如如何使用booList.Find()在field3中获取具有最早时间的元素

编辑道歉,我打算公开所有字段以简化示例。 我知道可以在linq中做到这一点,我想知道Find方法是否有简单的单行条件。

F#有方便的minBymaxBy操作符,我喜欢将它们实现为C#扩展方法,因为Linq库省略了它们。 它有点工作,但只有一点,它允许你避免复杂的表达,如

 var earliest = booList.First(b => b.Field3 == booList.Min(e => e.Field3)); 

相反,你可以输入:

 var earliest = booList.MinBy(b => b.Field3); 

一个简单的实现:

 static T MinBy(this IEnumerable sequence, Func keySelector) { bool first = true; T result = default(T); C minKey = default(C); IComparer comparer = Comparer.Default; //or you can pass this in as a parameter foreach (var item in sequence) { if (first) { result = item; minKey = keySelector.Invoke(item); first = false; continue; } C key = keySelector.Invoke(item); if (comparer.Compare(key, minKey) < 0) { result = item; minKey = key; } } return result; } 

这也比顶部的复杂表达式更有效,因为MinBy只重复一次序列,而表达式迭代不止一次且小于或等于两次。 当然,排序然后取第一项需要排序,即O(n log n),而这只是O(n)。

你需要通过公共属性公开field3(我们称之为Field3 ),但是你可以使用它:

 var earliest = booList.First(b => b.Field3 == booList.Min(e => e.Field3)); 

看看Enumerable.FirstEnumerable.Min

注意:它的时间复杂度为O(n ^ 2)(二次时间),因为它每次迭代都会通过Min遍历列表。 与Saeed Amiri的答案相比,足够大的集合将会出现严重的性能问题,该答案以O(n)(线性时间)运行。

使用OrderBy然后获取第一个元素

 var result = booList.OrderBy(p => p.field3).FirstOrDefault(); 

O(n)方法如下。 首先找到最小日期(对于field3),然后找到具有此最小日期的第一个对象:

 var minDate = booList.Min(x=>x.field3); var item = booList.First(x=>x.field3 == minDate); 

只需将您的财产公开。

据我所知,只使用List.Find就无法检索具有最小日期的BooClass对象。 当然你可以这样做:

 void Main() { List booList = new List { new BooClass { field3 = DateTime.MaxValue}, new BooClass { field3 = DateTime.Now }, new BooClass { field3 = DateTime.MinValue }}; var pred = GetPredicate(booList); var result = booList.Find(pred); } public Predicate GetPredicate(List boos) { var minDate = boos.Min(boo => boo.field3); return bc => bc.field3 == minDate; } 

(就像Saeed的解决方案 – 也有O(n)时间复杂度),但我想这会被认为是作弊……

如果您不想定义MinBy方法,可以像这样使用聚合:

 booList.Aggregate((currMin, test) => currMin < test ? currMin : test); 

要支持空列表,请使用null为聚合设置种子,如下所示:

 booList.Aggregate(null, (currMin, test) => null == currMin || currMin > test ? test : currMin); 

这个解决方案是O(n)