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

时间:2012-01-06 13:32:30

标签: c# .net list max min

说我有以下内容:

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

public List<BooClass> booList;

因此,例如,如何使用booList.Find()

在field3中获取具有最早时间的元素

编辑道歉,我的意思是公开所有字段以简化示例。我知道可以在linq中完成它,我想知道Find方法是否有一个简单的单行条件。

6 个答案:

答案 0 :(得分:8)

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<T, C>(this IEnumerable<T> sequence, Func<T, C> keySelector)
{
    bool first = true;
    T result = default(T);
    C minKey = default(C);
    IComparer<C> comparer = Comparer<C>.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)。

答案 1 :(得分:5)

您需要通过公共属性公开field3(我们称之为Field3),但您可以使用此方法:

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

查看Enumerable.FirstEnumerable.Min

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:2)

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

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

公开您的财产。

答案 4 :(得分:0)

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

void Main()
{
    List<BooClass> booList = new List<BooClass> { 
                        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<BooClass> GetPredicate(List<BooClass> boos)
{
    var minDate = boos.Min(boo => boo.field3);   
    return bc => bc.field3 == minDate;
}

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

答案 5 :(得分:0)

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

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

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

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

此解决方案为O(n)