'Find Max/Min element without using IComparable<T>

Say I have the following:

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

public List<BooClass> booList;

So for example how do I get the element with the earliest time in field3 using booList.Find()

Edit Apologies, I meant to make all the fields public for simplicity of the example. I know can do it in linq, I wondered if there is a simple single line condition for the Find method.



Solution 1:[1]

You'll need to expose field3 through through a public property (we'll call it Field3), but you could use this:

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

Take a look at Enumerable.First and Enumerable.Min

NOTE: That this has a time complexity of O(n^2) (quadratic time) because it is traversing the list via Min each iteration. A large enough collection will see serious performance issues compared to Saeed Amiri's answer, which runs in O(n) (linear time).

Solution 2:[2]

Use OrderBy Then get the first element

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

Solution 3:[3]

The O(n) approach is as follows. First find min date (for field3), then find first object with this min date:

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

Just make your property public.

Solution 4:[4]

As far as I can tell, there is no way to retrieve the BooClass object with the minimal date by just using List<T>.Find. Of course you can do this:

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;
}

(which - just like Saeed's solution - also has O(n) time complexity), but I guess that would be considered cheating...

Solution 5:[5]

If you don't want to define a MinBy method, you can use aggregate like so:

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

To support empty lists, seed the aggregate with null, like so:

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

This solution is O(n)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 shenhengbin
Solution 3 Gupta
Solution 4 afrischke
Solution 5