在不使用IComparable 的情况下查找Max / Min元素
说我有以下内容:
public Class BooClass { public int field1; public double field2; public DateTime field3; } public List booList;
那么例如如何使用booList.Find()在field3中获取具有最早时间的元素
编辑道歉,我打算公开所有字段以简化示例。 我知道可以在linq中做到这一点,我想知道Find方法是否有简单的单行条件。
F#有方便的minBy
和maxBy
操作符,我喜欢将它们实现为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.First
和Enumerable.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
就无法检索具有最小日期的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)