Find Max/Min element without using IComparable<T> - c#

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.

F# has handy minBy and maxBy operators, which I like to implement as C# extension methods, since the Linq library omits them. It's a bit of work, but only a bit, and it allows you to avoid complex expressions such as
var earliest = booList.First(b => b.Field3 == booList.Min(e => e.Field3));
Instead, you can type this:
var earliest = booList.MinBy(b => b.Field3);
A simple implementation:
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;
}
This is also somewhat more efficient than the complex expression at the top, since MinBy iterates the sequence exactly once, while the expression iterates more than once and less than or equal to twice. And, of course, sorting and then taking the first item requires sorting, which is O(n log n), while this is just O(n).
As noted by Saeed Amiri, this approach doesn't work if you are relying on Linq to SQL or any other IQueryable<> provider. (More precisely, it works inefficiently because it pulls the objects from the database and works on them locally.) For a solution that doesn't do this, see Saeed's answer.
You could also make an extension method based on that approach, but as I am on my phone at the moment I'll leave the implementation as the proverbial "exercise for the reader."

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).

Use OrderBy Then get the first element
var result = booList.OrderBy(p => p.field3).FirstOrDefault();

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.

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...

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)

Related

C# linq - Order by alphabetical, then by certain value [duplicate]

Can anyone explain what the difference is between:
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.OrderBy(sort2 => sort2.InvoiceOwner.FirstName)
.OrderBy(sort3 => sort3.InvoiceID);
and
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.ThenBy(sort2 => sort2.InvoiceOwner.FirstName)
.ThenBy(sort3 => sort3.InvoiceID);
Which is the correct approach if I wish to order by 3 items of data?
You should definitely use ThenBy rather than multiple OrderBy calls.
I would suggest this:
tmp = invoices.InvoiceCollection
.OrderBy(o => o.InvoiceOwner.LastName)
.ThenBy(o => o.InvoiceOwner.FirstName)
.ThenBy(o => o.InvoiceID);
Note how you can use the same name each time. This is also equivalent to:
tmp = from o in invoices.InvoiceCollection
orderby o.InvoiceOwner.LastName,
o.InvoiceOwner.FirstName,
o.InvoiceID
select o;
If you call OrderBy multiple times, it will effectively reorder the sequence completely three times... so the final call will effectively be the dominant one. You can (in LINQ to Objects) write
foo.OrderBy(x).OrderBy(y).OrderBy(z)
which would be equivalent to
foo.OrderBy(z).ThenBy(y).ThenBy(x)
as the sort order is stable, but you absolutely shouldn't:
It's hard to read
It doesn't perform well (because it reorders the whole sequence)
It may well not work in other providers (e.g. LINQ to SQL)
It's basically not how OrderBy was designed to be used.
The point of OrderBy is to provide the "most important" ordering projection; then use ThenBy (repeatedly) to specify secondary, tertiary etc ordering projections.
Effectively, think of it this way: OrderBy(...).ThenBy(...).ThenBy(...) allows you to build a single composite comparison for any two objects, and then sort the sequence once using that composite comparison. That's almost certainly what you want.
I found this distinction annoying in trying to build queries in a generic manner, so I made a little helper to produce OrderBy/ThenBy in the proper order, for as many sorts as you like.
public class EFSortHelper
{
public static EFSortHelper<TModel> Create<TModel>(IQueryable<T> query)
{
return new EFSortHelper<TModel>(query);
}
}
public class EFSortHelper<TModel> : EFSortHelper
{
protected IQueryable<TModel> unsorted;
protected IOrderedQueryable<TModel> sorted;
public EFSortHelper(IQueryable<TModel> unsorted)
{
this.unsorted = unsorted;
}
public void SortBy<TCol>(Expression<Func<TModel, TCol>> sort, bool isDesc = false)
{
if (sorted == null)
{
sorted = isDesc ? unsorted.OrderByDescending(sort) : unsorted.OrderBy(sort);
unsorted = null;
}
else
{
sorted = isDesc ? sorted.ThenByDescending(sort) : sorted.ThenBy(sort)
}
}
public IOrderedQueryable<TModel> Sorted
{
get
{
return sorted;
}
}
}
There are a lot of ways you might use this depending on your use case, but if you were for example passed a list of sort columns and directions as strings and bools, you could loop over them and use them in a switch like:
var query = db.People.AsNoTracking();
var sortHelper = EFSortHelper.Create(query);
foreach(var sort in sorts)
{
switch(sort.ColumnName)
{
case "Id":
sortHelper.SortBy(p => p.Id, sort.IsDesc);
break;
case "Name":
sortHelper.SortBy(p => p.Name, sort.IsDesc);
break;
// etc
}
}
var sortedQuery = sortHelper.Sorted;
The result in sortedQuery is sorted in the desired order, instead of resorting over and over as the other answer here cautions.
if you want to sort more than one field then go for ThenBy:
like this
list.OrderBy(personLast => person.LastName)
.ThenBy(personFirst => person.FirstName)
Yes, you should never use multiple OrderBy if you are playing with multiple keys.
ThenBy is safer bet since it will perform after OrderBy.

Dynamic LINQ discards applied OrderBy sortings except the latest applied [duplicate]

Can anyone explain what the difference is between:
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.OrderBy(sort2 => sort2.InvoiceOwner.FirstName)
.OrderBy(sort3 => sort3.InvoiceID);
and
tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.ThenBy(sort2 => sort2.InvoiceOwner.FirstName)
.ThenBy(sort3 => sort3.InvoiceID);
Which is the correct approach if I wish to order by 3 items of data?
You should definitely use ThenBy rather than multiple OrderBy calls.
I would suggest this:
tmp = invoices.InvoiceCollection
.OrderBy(o => o.InvoiceOwner.LastName)
.ThenBy(o => o.InvoiceOwner.FirstName)
.ThenBy(o => o.InvoiceID);
Note how you can use the same name each time. This is also equivalent to:
tmp = from o in invoices.InvoiceCollection
orderby o.InvoiceOwner.LastName,
o.InvoiceOwner.FirstName,
o.InvoiceID
select o;
If you call OrderBy multiple times, it will effectively reorder the sequence completely three times... so the final call will effectively be the dominant one. You can (in LINQ to Objects) write
foo.OrderBy(x).OrderBy(y).OrderBy(z)
which would be equivalent to
foo.OrderBy(z).ThenBy(y).ThenBy(x)
as the sort order is stable, but you absolutely shouldn't:
It's hard to read
It doesn't perform well (because it reorders the whole sequence)
It may well not work in other providers (e.g. LINQ to SQL)
It's basically not how OrderBy was designed to be used.
The point of OrderBy is to provide the "most important" ordering projection; then use ThenBy (repeatedly) to specify secondary, tertiary etc ordering projections.
Effectively, think of it this way: OrderBy(...).ThenBy(...).ThenBy(...) allows you to build a single composite comparison for any two objects, and then sort the sequence once using that composite comparison. That's almost certainly what you want.
I found this distinction annoying in trying to build queries in a generic manner, so I made a little helper to produce OrderBy/ThenBy in the proper order, for as many sorts as you like.
public class EFSortHelper
{
public static EFSortHelper<TModel> Create<TModel>(IQueryable<T> query)
{
return new EFSortHelper<TModel>(query);
}
}
public class EFSortHelper<TModel> : EFSortHelper
{
protected IQueryable<TModel> unsorted;
protected IOrderedQueryable<TModel> sorted;
public EFSortHelper(IQueryable<TModel> unsorted)
{
this.unsorted = unsorted;
}
public void SortBy<TCol>(Expression<Func<TModel, TCol>> sort, bool isDesc = false)
{
if (sorted == null)
{
sorted = isDesc ? unsorted.OrderByDescending(sort) : unsorted.OrderBy(sort);
unsorted = null;
}
else
{
sorted = isDesc ? sorted.ThenByDescending(sort) : sorted.ThenBy(sort)
}
}
public IOrderedQueryable<TModel> Sorted
{
get
{
return sorted;
}
}
}
There are a lot of ways you might use this depending on your use case, but if you were for example passed a list of sort columns and directions as strings and bools, you could loop over them and use them in a switch like:
var query = db.People.AsNoTracking();
var sortHelper = EFSortHelper.Create(query);
foreach(var sort in sorts)
{
switch(sort.ColumnName)
{
case "Id":
sortHelper.SortBy(p => p.Id, sort.IsDesc);
break;
case "Name":
sortHelper.SortBy(p => p.Name, sort.IsDesc);
break;
// etc
}
}
var sortedQuery = sortHelper.Sorted;
The result in sortedQuery is sorted in the desired order, instead of resorting over and over as the other answer here cautions.
if you want to sort more than one field then go for ThenBy:
like this
list.OrderBy(personLast => person.LastName)
.ThenBy(personFirst => person.FirstName)
Yes, you should never use multiple OrderBy if you are playing with multiple keys.
ThenBy is safer bet since it will perform after OrderBy.

Is there a dumb "all" in linq that evaluate all element?

Just like operator "&" will execute second condition even if first one is false,
I'm looking for the same thing for "All". I want the result of "All" but also want each elements to be evaluated. I called that a dumbAll in my example.
Here is an example of why I want this.
main
{
Object[] array = InitSomeArray();
AreObjectValid = array.dumbAll(o => Validate(o));
//other stuff with AreObjectValid
}
private bool Validate(object o)
{
bool IsValid = IsPositiveNumeric(o.ToString());
HighLighInvalidObjectWhenInvalid(o, IsInvalid: !IsValid);
return IsValid;
}
Does such thing exists in linq?
Note : I already know how to handle this without link.
You can use other query operators to achieve the same. One example is to count how many objects satisfy the condition.
Object[] array = InitSomeArray();
var count = array.Count(o => Validate(o));
var areObjectsValid = count == array.Length;
Not really, but you can achieve this aggregating the results.
var allValid = array.Aggregate(true, (acc, o) => acc & Validate(o));
I like this less than the Aggregate answer (and the Count answer as long as you have an array), but here:
AreObjectValid = array.Select(o => Validate(o)).ToArray().All(b => b);
or, as Asad points out,
AreObjectValid = array.Select(Validate).ToArray().All(b => b);
There's a bunch of LINQ answers here, but because I believe LINQ transformations should ideally be side effect free, here's an approach that uses a foreach and doesn't really sacrifice too much conciseness.
bool all = true;
foreach(var o in array)
all &= Validate(o);
This evaluates Validate for each value, and at the end all will be false if any of the values failed validation.

OrderBy with built in condition

I have a list of trips that I need to sort by the time at which they start. Certain trips do not have a time, and these are stored in the database as 12:00am. I am currently sorting the list as follows:
trips = trips.OrderBy(t => t.PickupTime);
The problem is that I want this to sort such that the 12:00am trips are put at the bottom of the list. Changing the database is not an option. Is there an elegant, clean way to tell the OrderBy to treat a time of 12:00am as being the highest value (probably use DateTime.MaxValue)?
I am aware that I could just use trips.Sort(delegate(..));, however I'd like to avoid this if possible. Also, I am not able to filter out these trips and then add them back on to the end. It wasn't received happily in code review.
You can use OrderBy with ThenBy:
trips = trips.OrderBy(t => t.TimeOfDay == (new DateTime(2014, 1, 1, 0, 0, 0)).TimeOfDay))
.ThenBy(t => t.PickupTime);
It will sort by is time 00:00:00 first, and because false comes before true in that kind of sorts, you'll get non-midnight values first. ThenBy will sort non-midnight values using standard DateTime comparison.
You could just use the conditional operator in your order by
var midnight = new DateTime(0).TimeOfDay;
trips = trips.OrderBy(t=> t.PickupTime.TimeOfDay == midnight ?
DateTime.MaxValue :
t.PickupTime);
I would suggest writing your own comparer that inherits IComparer and use that in the OrderBy in place of the default comparer. If you give me what type PickupTime is I can give you a more specific example, but here is a string comparer I have that ensures empty strings are last:
public class EmptyStringsAreLast : IComparer<string>
{
public int Compare(string x, string y)
{
if (String.IsNullOrEmpty(y) && !String.IsNullOrEmpty(x))
{
return -1;
}
else if (!String.IsNullOrEmpty(y) && String.IsNullOrEmpty(x))
{
return 1;
}
else
{
return String.Compare(x, y);
}
}
}
You would use this like:
//assuming Person has a Name property that is a string
IEnumerable<Person> bunchOfPeople = getPeople();
bunchOfPeople.OrderBy(p => p.Name, new EmptyStringsAreLast());

How to call Dictionary<K, V>.TryGetValue() where K : Predicate<T>, V : enum

I have Dictionary<Predicate<double>, SomeEnum>:
var dic = new Dictionary<Predicate<double>, SomeEnum>
{
{ (d) => d < 10, SomeEnum.Foo },
{ (d) => d > 90, SomeEnum.Bar }
};
I want to call TryGetValue(K, out V) against it like this:
dic.TryGetValue(99)
and receive
SomeStruct.Bar
But first param for TryGetValue() is Predicate<T>, not just T. How can I do what I want?
I found only a dirty workaround:
var kpv = dic.FirstOrDefault(p => p.Key(99));
if (kpv.Key != null)
var result = kpv.Value;
Are there other ways?
Or how to implement my idea properly? - declare a key not as a constant but like a segment.
There are a couple of things wrong here:
Predicate<double> is not an appropriate type to use as a TKey. The key for a dictionary is supposed to identify a value, not calculate a value.
This wouldn't make any sense using lambdas either. Because they are anonymous, you wouldn't get any equivalence, and won't be able use a dictionary.
See this code sample for an illustration:
Predicate<double> fn_1 = d => d == 34.0d;
Predicate<double> fn_2 = d => d == 34.0d;
// Note: There are not equal
if (fn_1 == fn_2)
Console.WriteLine("These are Equal?");
If anything, you could use a list of delegates and execute each one to find the ones that match, but at that point you must expect multiple results. If you only want to get a single result, then you have to consider which order the predicates are stored within your list.
Don't misuse KeyValuePair as a hack for not having Tuple<T1,T2>. It would be fairly easy to create a class that has both a Predicate and a SomeStruct. Look:
public class MySegment
{
public Predicate<double> Predicate {get;set;}
public SomeStruct Result {get;set;}
}
To go through a sequence of predicates, and find the matching ones would look like this:
...
List<MySegment> list = new List<MySegment>();
...
list.Add(new MySegment { Predicate = d => d < 10, Result = SomeStruct.Foo });
list.Add(new MySegment { Predicate = d => d > 90, Result = SomeStruct.Bar });
...
public IEnumerable<SomeStruct> GetResults(double input)
{
foreach (var item in list)
if (item.Predicate(input))
yield return item.Result;
}
If your list of predicates is not too long, you can just add them to a List<KeyValuePair<Predicate<T>, V>> and then perform a LINQ query:
var lt10 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d < 10, SomeStruct.Foo);
var gt90 = new KeyValuePair<Predicate<Double>, SomeStruct>(d => d > 90, SomeStruct.Bar);
var predicates = new List<KeyValuePair<Predicate<Double>, SomeStruct>>() { lt10, gt90 };
var result = predicates.FirstOrDefault(p => p.Key(99));
You're better off using SomeStruct? instead of SomeStruct, furthermore, since then FirstOrDefault will give an unambiguous result if it doesn't match any.
If your list is very long, you will want to consider some kind of data structure which permits queries on a range, like an Interval Tree.
This cannot be done using a Dictionary, because it relies on hash values to quickly determine where to look for a particular key.
As you've discovered, you can invoke the predicates directly, but that will require O(n) functions to be called, which is no better than using a List, or even a big if/then/else statement.
If your collection of potential predicates is too long for this to be an option, you'll need to create your own data structure to satisfy your purposes. If you're only planning to define values based on integer ranges, this shouldn't be difficult, but it could get out of hand if your predicates get more complex.
On a side note, the F# language, which has built-in support for this sort of definition using Match Expressions. I don't know how it goes about compiling the branches, but I assume it's fairly smart about it.
Edit
Here's an example of using a Match Expression in F# for something like this:
// Define the "choose" function
let choose value =
match value with
| v when v < 10 -> 1
| v when v > 90 -> 2
| _ -> 0
// Test the "choose" function
let choice1 = choose 5
let choice2 = choose 15
let choice3 = choose 95
The code above yields the following values:
choice1 = 1
choice2 = 0
choice3 = 2
I've never actually worked with F# before, so you'll have to look around for how to use a function from F# in a C# program.
You'll have to loop through your criteria and run each Predicate against the input to see if it matches. I don't see any reason to use a Dictionary here.

Categories