lambda expressions on populated lists - c#

There are a few posts on the site about how to order by using lambda expressions however I cannot seem to get mine to work. I am trying to reorder a list that is already populated. Am i wrong in thinking that i can rearrange the order of this list using lambada expressions?
QuarterMileTimes.OrderByDescending(c => c.PquartermileTime);
I was wondering if it's down to PquartermileTime being a string? I also tried this expression on a date
QuarterMileTimes.orderBy(c => c.RaceDay);
Still no luck where am I going wrong?

When you call OrderByDescending, the method returns a new IEnumerable<T> - it does not reorder the collection in place.
Try doing:
QuarterMileTimes = QuarterMileTimes.OrderByDescending(c => c.PquartermileTime).ToList();
(This is if your collection is a List<T>...)

The result of OrderByDescending (and all of the other Enumerable extension methods) is an IEnumerable<T> that projects the source data in the order you're describing. It does not alter the original data in any way.
If you prefer, you can use the ToList() extension method to create a new List<T> from that result and assign it back to the original variable.
QuarterMileTimes = QuarterMileTimes.OrderByDescending(/*...*/).ToList();
(This is assuming, of course, that QuarterMileTimes is a List<T>)
The gist of the answer is no, OrderByDescending does not alter the data source in any way.

You are assigning it to a new variable aren't you?
var sortedTimes = QuarterMileTimes.OrderByDescending(c => c.PquartermileTime);
It isn't like e.g. the List.Sort method, that sorts the existing list.
The result of the method has to be assigned to a variable.

OrderByDescending returns an IOrderedEnumerable<T> i.e. a new sequence with the items in the specified order. You'll have to re-assign QuarterMileTimes to get the behaviour you expect:
QuarterMileTimes = QuarterMileTimes.OrderByDescending(c => c.PquarterMileTime).ToList();
Alternatively you can just use the returned sequence separately, which is the usual approach.

QuarterMileTimes.OrderByDescending(c => c.PquartermileTime) returns a new enumerable, ordered by PquartermileTime. It does not reorder QuarterMileTimes in place.

Related

LINQ's ToDictionary method and the order of its elements

I have an IEnumerable of objects which I need to turn into a filtered dictionary. The key of the dictionary must be the object's name and the value must be one of its properties. I do so using LINQ's ToString method:
// dataPoints is of type IEnumerable
var filteredDictionary = dataPoints.Where(d => d.TypeName == typeNameOfInterest).ToDictionary(d => d.Name, d => d.Identifier)
I eventually turn the dictionary into a string as follows:
string.Join(Environment.NewLine, filteredDictionary.Select(d => $"{d.Key} : {d.Value};").ToArray()))
This works and the order of the elements in the string is the same as the order in the IEnumerable. I think that is what the documentation means by stating the following about the returned dictionary
The values within each group are in the same order as in source
Now, I need to get the first element from the dictionary. By first I mean the first one listed in the original IEnumerable. How would I need to do this? Can I rely on LINQ's first method to do so and be sure that it is the correct one? Would I need to construct a SortedDictionary before getting the first element?
I'm using the .NET Framework 4.7.2 and I never modify the content of the dictionary that I receive from ToDictionary.

OrderBy and List vs. IOrderedEnumerable

I ran across an unexpected problem with the following code.
List<string> items = new List<string>();
items = items.OrderBy(item => item);
This code generates the error:
Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'System.Collections.Generic.List'. An explicit conversion exists (are you missing a cast?)
It appears I can change items to be of type IEnumerable<string> and the error goes away. But I need to be able to add items to the list, which IEnumerable doesn't support.
Can someone help me understand this error, and what the easiest fix is? Is it safe to simply cast the result?
Why not just sort the list in place using the Sort() instance method; then you can add items to it later if you like:
List<string> items = GetSomeItems();
items.Sort();
Or, use an ordered collection like a binary search tree. SortedSet<T> might fit the bill, depending on your needs.
The solution suggested by the others:
items = items.OrderBy(item => item).ToList();
... creates another list with the original items in a new order. This is only useful if you need to preserve the original ordering for some other purpose; it's rather more wasteful of memory than sorting the list in place.
As far as understanding the error, it's simple: List<T> isn't a subtype of IOrderedEnumerable<T>, so there's no implicit reference conversion between the two. The explicit cast that the compiler suggests will satisfy the compiler, but it will fail at run time because the object returned by OrderBy<T> does not inherit from List<T>.
EDIT
An example of List<T>.Sort(Comparison<T>), assuming the type MyType has a Key property of some type type T where T : IComparable<T>:
List<MyType> items = GetSomeItems();
items.Sort((a, b) => a.Key.CompareTo(b.Key));
You need to convert the IEnumerable to a List. Try this:
items = items.OrderBy(item => item).ToList();
You need to use LINQ's ToList() method
items = items.OrderBy(item => item).ToList();
You can't cast directly from IEnumerable<> to List<>
Try this
items = items.OrderBy(item => item).ToList();
For sorting a list of strings you do not need Linq in the first place - just use Sort():
List<string> items = new List<string>();
//add items here
items.Sort();
OrderBy() is an extension method of IEnumerable - and not List.
When the compiler encounters the OrderBy() extension method, it casts the range variable to an IOrderedEnumerable where it can perform the required sorting via CreateOrderedEnumerable method using IComparer et al. Once sorted, the compiler spits out the variable as IEnumerable - usually.
Suggestion: use the var keyword to type 'items' in the LinQ clause.
Certainly the options offered above using the Sort() and ToList() methods will work - however, using them involves greedy operators and you lose the advantage of lazy loading.
Here's a good breakdown here:
C# Sort and OrderBy comparison between running Sort() and OrderBy().

Lambda Expressions and Method calls

Hi I have a collection of Objects in a Listview and i need to know if i can iterate through them with a lambda expression. and call a method on it in the expression.
Lets say i need to save a group of people to a database.
List<People> someList;
someList.Select(person => person.Save());
is this possible to do? so far i have not been able to get it working.
thanks
You can use the ForEach method of a generic list:
List<People> someList;
someList.ForEach(person => person.Save());
someList.ForEach(p => p.Save());
Sounds like you want a foreach statement:
foreach(People p in someList)
{
p.Save();
}
But if you really want to do it in lambda expressions and LINQ, then your problem with the above code is that .Select(...) returns an IEnumerable/IQueryable, which creates a new query but doesn't execute your lambda expressions.
You could force the lambda to evaluate by calling an extension method that forces an enumeration of the data the IEnumerable/IQueryable represents. For instance by doing:
someList.Select(person => person.Save()).Count();
but this also assumes your Save() method returns non-void.
Edit:
As others have pointed out, if you're working specifically with a List<>, then you can also do:
someList.ForEach(person => person.Save());

Sort an array of strings in C#

How can I sort an array of strings using the OrderBy function? I saw I need to implement some interfaces...
You can sort the array by using.
var sortedstrings = myStringArray.OrderBy( s => s );
This will return an instance of Ienumerable. If you need to retain it as a array, use this code instead.
myStringArray = myStringArray.OrderBy( s => s ).ToArray();
I'm not sure what you are referring to when you said that you have to implement some interfaces, but you do not have to do this when using the IEnumerable.OrderBy. Simply pass a Func<TSource, TKey> in the form of a lambda-expression.
OrderBy won't sort the existing array in place. If you need to do that, use Array.Sort.
OrderBy always returns a new sequence - which of course you can convert to an array and store a reference to in the original variable, as per Øyvind's answer.
To sort inside an existing array, call Array.Sort(theArray).
Re your comment on interfaces: you don't need to add any interfaces here, since string is well supported; but for custom types (of your own) you can implement IComparable / IComparable<T> to enable sorting. You can also do the same passing in an IComparer / IComparer<T>, if you want (or need) the code that provides the ordering to be separate to the type itself.
Linq has two (syntax) ways to sort an array of strings.
1:
string[] sortedStrings = unsortedStrings.OrderBy(s => s).ToArray();
This syntax is using a Lambda Expressions if you don't know what s => s means.
2:
sortedStrings = (from strings in unsortedStrings
orderby strings
select strings).ToArray();
This example looks a bit like a SQL statement and is probably easier to read if you are new with Linq.
ToArray() converts the IOrderedEnumerable<string> to as string[] in this case.

Sort list by property/anonymous function?

I've got a list defined like this...
var sets = new List<HashSet<int>>(numSets);
Why isn't there an overload so I can sort it like this?
sets.Sort(s => s.Count);
I want the largest set first. What's the easiest way to do that?
Because List<T> class was introduced in .NET 2.0 and the designers of this class decided so. You could use the OrderByDescending extension method:
sets = sets.OrderByDescending(s => s.Count).ToList();
Try this:
sets.Sort((setA, setB) => setB.Count.CompareTo(setA.Count));
This uses the Sort(Comparison<T> comparison) overload of List<T>.Sort.
The fact that the expression compares B with A rather than A with B is what produces the descending-by-count order that you require.
The reason your code doesn't work is because List<T>.Sort, unlike Enumerable.OrderByDescending, does not have an overload that accepts a Func<TSource, TKey> key-selector.
#Darin Dimitrov's technique of using OrderByDescending is fine too, but note that this will create a sorted list out of place and reassign the reference you have to the original list to the new, sorted one.

Categories