How to order Dates in Linq? - c#

I am using C# and LINQ, I have some Date in type Date
At the moment I am using this script to order a list by date of start, from the earlier to the latest.
With the following code my events are not sorted:
events.OrderBy(x => x.DateTimeStart).ToList();
return events.AsQueryable();
What could be wrong here?

events.OrderBy(x => x.DateTimeStart).ToList() creates a new list, and you don't return it.
You probably want something like
return events.OrderBy(x => x.DateTimeStart).ToList();

events.OrderBy(x => x.DateTimeStart): Declare a query that sorts events by property DateTimeStart. The query is not performed yet.
events.OrderBy(x => x.DateTimeStart).ToList();: Process the previous query. Iterate through all events, checks their DateTimeStart, sort them and safe result as a List and then... discard the result! Because you didn't safe it. Compare it with something like that:
int a = 0;
a + 1;
b = a; // b is 0
return events.AsQueryable();: Here you are returning your original events instead of sorted.
You should write your code as follows:
return events.OrderBy(x => x.DateTimeStart).ToList().AsQueryable();
That version will create static list of sorted events. If now you change the events list, the result will not take into account your changes.
The second solution is:
return events.OrderBy(x => x.DateTimeStart).AsQueryable();
That version will do no work. It just declarates a manner to sort events and returns that manner as IQueryable. If you use returned value in future code it will always contain all sorted events even if you add new ones prior to use that.

store your orderedevents in a variable and return this variable asQueryable();
var orderedEvents = events.OrderBy(x => x.DateTimeStart).ToList();
return orderedEvents.AsQueryable();
or if you dont need that variable return your ordered events directly.
return events.OrderBy(x => x.DateTimeStart).ToList().AsQueryable();

Related

Advanced filtering in linq

I'm here to ask a question on how would a code look like when it came to advanced filtering in C# with LINQ. I have experience with Linq, but this is something that was out of my understanding.
Lets say we have a class Item that has properties (string)Name, (bool)New and (int)Price and users would have to input their filters and get the results they need.
Lets say we put 5 objects inside a list list that is a List of Items.
new Item("Pen",true,12);
new Item("PostIt",false,35);
new Item("Phone",true,140);
new Item("Watch",true,5);
new Item("Lavalamp",false,2);
Now I woud like to process this information to get.. All New times that cost over 10. I know I can do this with
List<Item> Results = list.where(item => item.Price> 10 && item.New).ToList();
but what if a user wants to get all items that cost over 10 regardless of being new or not.. I can't change the query during runtime to fit the needs and I don't think that making a query for every possible combination of input parameters is a right way to do this... Can someone give me an example on how this should be done?
You can define base query
var result = list.Where(item=> item.Price > 10); //DON'T Call ToList() here
if(someCondition)
result = result.Where(item=> item.New);
//in the end you are calling
return result.ToList();
Like #MikeEason said you don't want to call ToList() on your first result because this will execute the query. Your goal is to build the complex query and execute it only once. Because of that this is done when you return the result.
If you only have those three conditions then you can build your query in several steps:
IEnumerable<Item> result=list;
int Price=10;
bool FilterByPrice, bool FilterByNew;//Set this variables in your environment
if(FilterByPrice)
result=result.Where(item => item.Price> Price);
if(FilterByNew)
result=result.Where(item => item.New);
Your query will be executed when you call ToList method or went you iterate over the query result thanks to deferred execution.
So let's say your items exist in your database and you want to query them. The user has a checkbox, if he wants to see only new items or all of them. If the box is checked you set a bool value for it.
// Compose the query
var results = _db.Where(item => item.Price > 10 );
// Still composing
if (onlyNewItems)
{
results = results.Where(item => item.New);
}
// ToList() executes the query, data is returned;
return results.ToList();
This does not run the query twice. In fact, until you materialize your query, you are still composing it. If you would return it now, it would be of type IQueryable<T>. Only after you call .ToList(), is your query actually executed and you get an IEnumerable<T> in this case a List<T> back.
List<Item> Results = list.where(item => item.Price > 10
&& (condition ? item.New : true)).ToList();
you can extend this way. just pass true if your condition is false and it is like nothing is happened.

Filter in linq with ID's in a List<int>

I need do a filter that request data with a parameter included in a list.
if (filter.Sc.Count > 0)
socios.Where(s => filter.Sc.Contains(s.ScID));
I try on this way but this not work, I tried also...
socios.Where( s => filter.Sc.All(f => f == s.ScID));
How I can do a filter like this?
socios.Where(s => filter.Sc.Contains(s.ScID));
returns a filtered query. It does not modify the query. You are ignoring the returned value. You need something like:
socios = socios.Where(s => filter.Sc.Contains(s.ScID));
but depending on the type of socios the exact syntax may be different.
In addition to needing to use the return value of your LINQ .Where(), you have a potential logic error in your second statement. The equivalent logic for a .Contains() is checking if Any of the elements pass the match criteria. In your case, the second statement would be
var filteredSocios = socios.Where( s => filter.Sc.Any(f => f == s.ScID));
Of course if you can compare object-to-object directly, the .Contains() is still adequate as long as you remember to use the return value.

How to optimize a LINQ with minimum and additional condition

Asume we have a list of objects (to make it more clear no properties etc.pp are used)
public class SomeObject{
public bool IsValid;
public int Height;
}
List<SomeObject> objects = new List<SomeObject>();
Now I want only the value from a list, which is both valid and has the lowest height.
Classically i would have used sth like:
SomeObject temp;
foreach(SomeObject so in objects)
{
if(so.IsValid)
{
if (null == temp)
temp = so;
else if (temp.Height > so.Height)
temp = so;
}
}
return temp;
I was thinking that it can be done more clearly with LinQ.
The first approach which came to my mind was:
List<SomeObject> sos = objects.Where(obj => obj.IsValid);
if(sos.Count>0)
{
return sos.OrderBy(obj => obj.Height).FirstOrDefault();
}
But then i waas thinking: In the foreach approach i am going one time through the list. With Linq i would go one time through the list for filtering, and one time for ordering even i do not need to complete order the list.
Would something like
return objects.OrderBy(obj => obj.Height).FirstOrDefault(o => o.IsValid);
also go twice throught the list?
Can this be somehow optimized, so that the linw also only needs to run once through the list?
You can use GroupBy:
IEnumerable<SomeObject> validHighestHeights = objects
.Where(o => o.IsValid)
.GroupBy(o => o.Height)
.OrderByDescending(g => g.Key)
.First();
This group contains all valid objects with the highest height.
The most efficient way to do this with Linq is as follows:
var result = objects.Aggregate(
default(SomeObject),
(acc, current) =>
!current.IsValid ? acc :
acc == null ? current :
current.Height < acc.Height ? current :
acc);
This will loop over the collection only once.
However, you said "I was thinking that it can be done more clearly with LinQ." Whether this is more clear or not, I leave that up to you to decide.
You can try this one:
return (from _Object in Objects Where _Object.isValid OrderBy _Object.Height).FirstOrDefault();
or
return _Objects.Where(_Object => _Object.isValid).OrderBy(_Object => _Object.Height).FirstOrDefault();
Would something like
return objects.OrderBy(obj => obj.Height).FirstOrDefault(o => o.IsValid);
also go twice throught the list?
Only in the worst case scenario, where the first valid object is the last in order of obj.Height (or there is none to be found). Iterating the collection using FirstOrDefault will stop as soon as a valid element is found.
Can this be somehow optimized, so that the linw also only needs to run
once through the list?
I'm afraid you'd have to make your own extension method. Considering what I've written above though, I'd consider it pretty optimized as it is.
**UPDATE**
Actually, the following would be a bit faster, as we'd avoid sorting invalid items:
return object.Where(o => o.IsValid).OrderBy(o => o.Height).FirstOrDefault();

Sort based on function

I have a method that given 2 strings he returns a number (between 0 and 100) which represents is how alike they are, being 0 "not similar at all" and 100 "they are the same"
Now the thing is that i have a list of County (string name, GeoRef coordinates, string Mayor) which i would like to sort based on the return of my function...
im looking for something like myList.Sort(f=>MyScoreEvaluator("York",f.Name))
Can anyone tell me how to do so?
Edit1: I dont think that the method "Sort" is quite i want... Sort compare itens inside of the list... i want to compare the itens of the list against a external info and based on that result sort the items
The OrderBy and OrderByDescending are returning the same item order...
Edit2: Heres is the code of the OrderBy I'm using: aux.OrderBy(f => StringComparisonHelper.HowAlike(f.Name, countyNameSearched));
You can use OrderBy, and re-assign your list:
list = list.OrderBy(f => MyScoreEvaluator("York", f.Name))
You could just use OrderBy:
list.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or Implement a custom Comparer:
public static int SortByName(County x, County y)
{
return x.Name.CompareTo(y.Name);
}
Usage:
list.Sort(new Comparison<County>(SortByName))
There is an OrderBy in LINQ:
var sorted = myList.OrderBy(f => MyScoreEvaluator("York", f.Name))
Or to sort descendingly:
var sortedDesc = myList.OrderByDescending(f => MyScoreEvaluator("York", f.Name))
It's very easy to use the LINQ OrderBy extension (see others' answers).
If you want to use Sort, it would be:
myList.Sort((x, y) => MyScoreEvaluator("York", x.Name)
.CompareTo(MyScoreEvaluator("York", y.Name)));
This assumes that myList is a System.Collections.Generic.List<>.
If you want the other sort direction, swap x and y on one side of the lambda arrow =>, of course.
EDIT:
Remember .Sort method on List<> modifies the same instance. The return type of Sort method is void. On the other hand, OrderBy creates a new IEnumerable<> on which you can call .ToList() to get a new list object. The old object is unchanged. You might assign the new object to the variable that held the original list. Other variables that reference the old object won't be affected by that. Example:
myList = myList.OrderBy(f => MyScoreEvaluator("York", f.Name)).ToList();
NEW EDIT:
If performance is an issue, it's not clear which of these two to use. The OrderBy method calls the MyScoreEvaluator only once per item in your original list. The Sort method as presented here, calls MyScoreEvaluator a lot more times, because it doesn't "remember" the result of each MyScoreEvaluator call (the Comparison<> delegate instance is a black box to the Sort algorithm). So if it wants to compare "Fork" and "Kork", it calls MyScoreEvaluator twice. Then afterwards if it wants to compare "Kork" and "Yorc", it does the "Kork" MyScoreEvaluator again. On the other hand, the sort algorithm of List<>.Sort is superior to that of OrderBy.

Using Linq lambdas, how can I get the first item in a two-key-sorted list?

I know this is simple, but my mind is playing tricks on me right now. If we have a flat list of objects with the properties GroupSortIndex and ItemSortIndex (within the group) and we want to find the first item in the list, what's the Linq/lambda for that?
About all I can think of is (meta, not literal code...)
var soughtItem = Source.OrderBy(ItemSortIndex).OrderBy(GroupSortIndex).ToList()[0]
...but that just looks so wrong to me for some reason.
Read post : Default Extension methods to get difference between first and firstordefault
you can use FirstOrDefualt() or First() function
var soughtItem = Source.OrderBy(ItemSortIndex).
ThenBy(GroupSortIndex).FirstOrDefualt();
if(soughtItem !=null)//advantage of using firstordefault
{
}
its better to use FirstOrDefualt because if there is no data it will return null intead of excetipn
You can use IOrderedEnumerable.ThenBy (Note: an IOrderedEnumerable is returned from IEnumerable.OrderBy):
var firstItem = source.OrderBy(s => s.GroupSortIndex)
.ThenBy(s => s.ItemSortIndex)
.First();
This orders first by the group and then by the item. You should use FirstOrDefault if the sequence can be empty. Otherwise First raises an exception.
(i've assumed that you want to order first by group and then by the item instead, since the ItemSortIndex is the index of the item within the group(as mentioned))
var soughtItem = Source
.OrderBy(ItemSortIndex)
.ThenBy(GroupSortIndex).First();
If ItemSortIndex and GroupSortIndex are properties instead of functions, then you need:
var soughtItem = Source
.OrderBy(i => ItemSortIndex)
.ThenBy(i => GroupSortIndex).First();

Categories

Resources