C# secondary sort with lambda expression - c#

How to sort a list in C# with secondary sorting.
For example if I have a class called Student with two fields FirstName and LastName.
Until now I've sorted the list as follows:
MyStudents.Sort((s1, s2) => s1.LastName.CompareTo(s2.LastName));
I wanted to know how can I sort the list first by last name and then by first name.
Thanks.

Sort doesn't have that capability.
Linq can do this simply:
MyStudents.OrderBy(s => s.LastName).ThenBy(s => s.FirstName)
Ensure you are using at least .NET 3.5 and have the System.Linq namespace referenced.

You can use LINQ:
MyStudents.OrderBy(e => e.LastName).ThenBy(e => e.FirstName);

You can use OrderBy and ThenBy
var sortedList = MyStudents.OrderBy(s => s.LastName).ThenBy(s=> s.FirstName);
note that OrderBy and ThenBy does not change the order of MyStudents list and they return an IEnumerable<Student>, so if you need a List<Student> use ToList().

Related

nested foreach as LINQ with chained linked List

I have a List within a list. I only need the one value from the list and can do obtain my result with a nested foreach but I want to use a LINQ query of some sort.
my code:
var myCity = from c in CountryLists
select (from city in c.stateList
where city.name == passedInValue
select city.name).FirstorDefault();
This returns myCity as a list of some sort with all values as null EXCEPT for where the match was found.
i don't want to have to walk through the city list to find the name. How can I have only one value in myCity; either null or the desired name?
First, use SelectMany to flatten the list, then FirstOrDefault to filter:
CountryList.SelectMany(c => c.stateList).FirstOrDefault(d => d.Name == passedInValue);
Note that because FirstOrDefault can take a predicate, you don't actually need the Where clause.
How about using SelectMany:
var city = CountryLists
.SelectMany(x => x.stateList)
.FirstOrDefault(x => x.name == passedInValue);
You can use SelectMany as other have pointed out (and I prefer that solution myself), however if you'd like the query syntax, you can use multiple from clauses (check the MSDN documentation for more examples):
var city = (from c in CountryLists
from city in c.stateList
where city.name == passedInValue
select city.name).FirstOrDefault();
It is equivalent to the SelectMany method solution, it uses it under the covers anyway.

Order Subgrouping in Linq Query

I have a problem with a Linq query. I have a query formatted like the following which returns a result set that is grouped by the top level and is ordered by the top level strings alphabetically. I want to return a result set that is ordered by both the top level strings (so the group by clause is ordered alphabetically) and the sub level strings are also in alphabetical order (so each sub group is also alphabetical). What I'm expecting is something like the following:
A
A
B
C
B
A
B
C
My query is something like the following:
records.results
.Where(table => string.Compare(table, expected) == 0)
.OrderByDescending(table => table.name)
.GroupBy(table => table.name);
.OrderBy(group => group.Key);
I expect the OrderByDescending statement to change the ordering of the individual records in the groupby clause but it isn't effecting anything. I am receiving ordered results for the groups though. Any help is appreciated!
Your problem is that your final OrderBy statement is ordering the groups themselves. What you want to do is order the elements of each group. Try:
records.results
.Where(table => string.Compare(table, expected) == 0)
.OrderByDescending(table => table.name)
.GroupBy(table => table.name);
.Select(g => g.OrderByDescending(element => element.Name);
I'm not sure of the name of the property by which you want to order the groups is, but here I'm assuming it is Name.
The key here is to remember that IGrouping is itself an IEnumerable. See the documentation of IGrouping here and note that it implements IEnumerable, so we can call OrderByDescending on each group in your IEnumerable of IGroupings, which is the return type of Enumerable.GroupBy(documented here).
Since OrderByDescending will return an IEnumerable (instead of an IGrouping), this operation will lose the Key property on each group. If you need that, then you might want to simply select an anonymous type to keep the key. Replace the final Select with:
.Select(g => new { g.Key, elements = g.OrderByDescending(element => element.Name) });
Alternatively, you could write some logic that would intantiate an IGrouping here by following the directions here.

Return only values contained in list C#

I have a List<int> ListOfIDs containing some numbers which are IDs.
I have a List<CustomClass> ListOfObjects containing some objects, which properties reflecting their IDs.
I've searched high and low for a Linq query that will allow me to return from my List a sublist of only those objects which have an ID that is contained within the List.
My attempt does not compile and I cannot seem to correct the syntax :
List<CustomClass> SubList = ListOfObjects.Where(ListOfIDs.Contains(p => p.ID))
Thanks very much.
I think you want to do like this?
List<CustomClass> SubList = ListOfObjects
.Where(obj => ListOfIDs.Contains(obj.ID))
.ToList();
I think this is what you need:
List<CustomClass> SubList = ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Don't forget to call ToList() in the end.
Also consider using HashSet for ListOfIDs, because complexity of Contains operation is just O(1). But, well it depends on how much data you have.
Here's the correct syntax for what you're trying to do:
... ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Though this might be faster that the Where(Contains) method:
var sublist = (
from obj in ListOfObjects
join id in ListOfIDs on id equals obj.ID
select obj ).ToList();
Try to use this piece of code snippet:
List<CustomClass> SubList = ListOfObjects.Where(o => ListOfIDs.Contains(o.ID))
.ToList();

IQueryable order by two or more properties

I am currently ordering a list of custom objects using the IQueryable OrderBy method as follows:
mylist.AsQueryable().OrderBy("PropertyName");
Now I am looking to sort by more than one property. Is there any way to do that?
Thanks,
Yannis
OrderBy(i => i.PropertyName).ThenBy(i => i.AnotherProperty)
In OrderBy and ThenBy you have to provide keySelector function, which chooses key for sorting from object. So if you know property name only at runtime then you can make such function with Reflection like:
var propertyInfo = i.GetType().GetProperty("PropertyName");
var sortedList = myList.OrderBy(i => propertyInfo.GetValue(i, null))
But it will be slower, then direct access to property. Also you can "compile" such function on the fly with Linq.Expressions and it will work faster than reflection but it is not very easy. Or you can use CollectionViewSource and their sorting ablilities in WPF.
And don't forget that OrderBy() returns sorted enumerable and it does not sort your existed List inplace. In your example you did not save sorted list to variable.
You could use .ThenBy:
var result = mylist
.AsQueryable()
.OrderBy(x => x.PropertyName)
.ThenBy(x => x.SomeOtherProperty);
You probably want to use the ThenBy extension method to be able to sort by multiple fields
return myList.AsQueryable().OrderBy(m=>m.Property1).ThenBy(m => m.Property2);
If you want dynamic Linq, look at LinqKit. I recently implemented Microsoft's dynamic Linq library from here and was able to sort by two fields using a string.
Awesome stuff! Not sure if this will be in .NET 5 or not.
As others have suggested, you can use 'ThenBy'. If you want to convert a string to a different value before using it, this is possible too, for example...
var sortedSystemTestResultsList = systemTestResultsList.OrderBy(s =>
{
DateTime dt;
if (!DateTime.TryParse(s.testPointCompletedDate, out dt)) return DateTime.MaxValue;
return dt;
}).ThenBy(s =>
{
Int32 tpID;
if (!Int32.TryParse(s.testRunResultID, out tpID)) return Int32.MaxValue;
return tpID;
});

Sorting a list in C# (with various parameters)

I have a list of objects. That objects have various field, e.g. age and name
Now sometimes I'd like to sort the list by names and sometimes by age. Additional sometimes increasing order and sometimes decreasing order.
Now I understand that i should implement the Comparable interface in my object and override the CompareTo method.
But how can i do this when i want to support various sorting orders?
Do i have to set the sorting order in my object or is it somehow possible to pass the sorting order by the sort method call?
The method call can do everything; no need for a comparer:
list.Sort((x,y)=>string.Compare(x.Name,y.Name));
list.Sort((x,y)=>y.Age.CompareTo(x.Age)); // desc
list.Sort((x,y)=>x.Age.CompareTo(y.Age)); // asc
Note the second is descending, by swapping x/y in the compare.
If you're using List<T> and you want to sort the list in place, then the Sort function provides an overload that accepts a Comparison<T>. You can use this to provide different comparisons for a list.
For example, to sort on Age:
list.Sort((x, y) => x.Age.CompareTo(y.Age));
To sort on Name:
list.Sort((x, y) => string.Compare(x.Name, y.Name));
To sort in descending order, simply reverse the parameters.
Alternatively, you could use LINQ to create various queries against your list that provide the results in whatever order you like, but this won't have any effect upon the underlying list (whether that's bad or good is up to you):
var byAge = list.OrderBy(x => x.Age);
var byName = list.OrderBy(x => x.Name);
To sort in descending order, use OrderByDescending in place of OrderBy.
You can also just use LINQ to handle this:
var sortedByAge = myList.OrderBy(i => i.Age);
var sortedByName = myList.OrderBy(i => i.Name);
If you want to handle sorting in place, you can use List<T>.Sort(Comparison<T>):
// Sort by Age
myList.Sort( (l, r) => l.Age.CompareTo(r.Age) );
// Sort by Name
myList.Sort( (l, r) => l.Name.CompareTo(r.Name) );
You can sort your objects data with linq
something like this
var query = from cust in customers
orderby cust.Age ascending
select cust;
You can also use
list.OrderByDescending(a => a.Age);
or
list.OrderByAscending(a => a.Age);

Categories

Resources