OrderBy and List vs. IOrderedEnumerable - c#

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

Related

Does Linq's IEnumerable.Select return a reference to the original IEnumerable?

I was trying to clone an List in my code, because I needed to output that List to some other code, but the original reference was going to be cleared later on. So I had the idea of using the Select extension method to create a new reference to an IEnumerable of the same elements, for example:
List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => s);
Now after doing ogList.Clear(), I was surprised to see that my new enumerable was also empty.
So I started fiddling around in LINQPad, and saw that even if my Select returned different objects entirely, the behaviour was the same.
List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => 5); // Doesn't return the original int
enumerable.Count().Dump(); // Count is 3
ogList.Clear();
enumerable.Count().Dump(); // Count is 0!
Note that in LINQPad, the Dump()s are equivalent to Console.WriteLine().
Now probably my need to clone the list in the first place was due to bad design, and even if I didn't want to rethink the design I could easily clone it properly. But this got me thinking about what the Select extension method actually does.
According to the documentation for Select:
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
So then I tried adding this code before clearing:
foreach (int i in enumerable)
{
i.Dump();
}
The result was still the same.
Finally, I tried one last thing to figure out if the reference in my new enumerable was the same as the old one. Instead of clearing the original List, I did:
ogList.Add(4);
Then I printed out the contents of my enumerable (the "cloned" one), expecting to see '4' appended to the end of it. Instead, I got:
5
5
5
5 // Huh?
Now I have no choice but to admit that I have no idea how the Select extension method works behind the scenes. What's going on?
List/List<T> are for all intents and purposes fancy resizable arrays. They own and hold the data for value types such as your ints or references to the data for reference types in memory and they always know how many items they have.
IEnumerable/IEnumerable<T> are different beasts. They provide a different service/contract. An IEnumerable is fictional, it does not exist. It can create data out of thin air, with no physical backing. Their only promise is that they have a public method called GetEnumerator() that returns an IEnumerator/IEnumerator<T>. The promise that an IEnumerator makes is simple:
some item could be available or not at a time when you decide you need it. This is achieved through a simple method that the IEnumerator interface has: bool MoveNext() - which returns false when the enumeration is completed or true if there was in fact a new item that needed to be returned. You can read the data through a property that the IEnumerator interface has, conveniently called Current.
To get back to your observations/question: as far as the IEnumerable in your example is concerned, it does not even think about the data unless your code tells it to fetch some data.
When you are writing:
List<int> ogList = new List<int> {1, 2, 3};
IEnumerable<int> enumerable = ogList.Select(s => s);
You are saying: Listen here IEnumerable, I might come to you asking for some items at some point in the future. I'll tell you when I will need them, for now sit still and do nothing. With Select(s => s) you are conceptually defining an identity projection of int to int.
A very rough simplified, non-real-life implementation of the select you've written is:
IEnumerable<T> Select(this IEnumerable<int> source, Func<int,T> transformer) something like
{
foreach (var i in source) //create an enumerator for source and starts enumeration
{
yield return transformer(i); //yield here == return an item and wait for orders
}
}
(this explains why you got a 5 when expecting a for, your transform was s => 5)
For value types, such as the ints in your case: If you want to clone the list, clone the whole list or part of it for future enumeration by using the result of an enumeration materialized through a List. This way you create a list that is a clone of the original list, entirely detached from its original list:
IEnumerable<int> cloneOfEnumerable = ogList.Select(s => s).ToList();
Later edit: Of course ogList.Select(s => s) is equivalent to ogList. I'm leaving the projection here, as it was in the question.
What you are creating here is: a list from the result of an enumerable, further consumed through the IEnumerable<int> interface. Considering what I've said above about the nature of IList vs IEnumerable, I would prefer to write/read:
IList<int> cloneOfEnumerable = ogList.ToList();
CAUTION: Be careful with reference types. IList/List make no promise of keeping the objects "safe", they can mutate to null for all IList cares. Keyword if you ever need it: deep cloning.
CAUTION: Beware of infinite or non-rewindable IEnumerables
Provided answers explain why you are not obtaining a cloned list (due to deferred execution of some LINQ extension methods).
However, keep in mind that list.Select(e => e).ToList() will get a real clone only when dealing with value types such as int.
If you have a list of reference types you will receive a cloned list of references to existent objects. In this case you should consider one of the solutions provided here for deep-cloning or my favorite from here (which might be limited by object inner structure).
You have to be aware that an object that implements IEnumerable does not have to be a collection itself. It is an object that makes it possible to get an object that implements IEnumerator. Once you have the enumerator you can ask for the first element and for the next element until there are no more next elements.
Every LINQ function that returns an IEnumerable is not the sequence itself, it only enables you to ask for the enumerator. If you want a sequence, you'll have to use ToList.
There are several other LINQ functions that do not return an IEnumerable, but for instance a Dictionary, or only one element (FirstOrDefault(), Max(), Single(), Any(). These functions will get the enumerator from the IEnumerable and start enumerating until they have the result. Any will only have to check if you can start enumerating. Max will enumerate over all elements and remember the largest one. etc.
You'll have to be aware: as long as your LINQ statement is an IEnumerable of something, your source sequence is not accessed yet. If you change your source sequence before you start enumerating, the enumeration is over your changed source sequence.
If you don't want this, you'll have to do the enumeration before you change your source. Usually this will be ToList, but this can be any of the non-deferred function: Max(), Any(), FirstOrDefault(), etc.
List<TSource> sourceItems = ...
var myEnumerable = sourceItems
.Where(sourceItem => ...)
.GroupBy(sourceItem => ...)
.Select(group => ...);
// note: myEnumerable is an IEnumerable, it is not a sequence yet.
var list1 = sourceItems.ToList(); // Enumerate over the sequence
var first = sourceItems.FirstOrDefault(); // Enumerate and stop after the first
// now change the source, and to the same things again
sourceItems.Clear();
var list1 = sourceItems.ToList(); // returns empty list
var first = sourceItems.FirstOrDefault(); // return null: there is no first element
So every LINQ function that does not return IEnumerable, will start enumerating over sourceItems as the sequence is at the moment that you start enumerating. The IEnumerable is not the sequence itself.
This is an enumerable.
var enumerable = ogList.Select(s => s);
If you iterate through this enumerable, LINQ will in turn iterate over the original resultset. Each and every time. If you do anything to the original enumerable, the results will also be reflected in your LINQ calls.
If you need to freeze the data, store it in a list instead:
var enumerable = ogList.Select(s => s).ToList();
Now you've made a copy. Iterating over this list will not touch the original enumerable.

Item removed from one collection is removed from second collection implicitly too, why?

This is was big confusion for me. I have following code:
IEnumerable<WorkflowStateData> data = model.Data.Except(
request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
new WorkflowStateDataEqualityComparer());
foreach (var item in data) // makes the comparison (and exception) based on the value of property 'Key'
{
model.Data.Remove(item);
stateDataSet.Remove(item);
}
This should create new collection of items and use it for enumeration using foreach. It should remove items from original collection, but it removes it both from original collection and from "data" collection and throws InvalidOperationException: Collection was modified. Why?
Enumerable.Except returns an IEnumerable<T> of the set difference but it does it deferred which means it is still only the query. So everytime you would "touch" data you would execute this query. So if you want a "materialized" collection you have to use ToList, ToArray, ToLookup or ToDictionary (or add the result from a foreach-loop into another collection).
Normally you should even get an exception if you try to modify the collection that you are currently enumerating in a foreach. So you could use ToList to create a real collection which is not afffected if you modify it's source:
List<WorkflowStateData> data = model.Data.Except(
request.WorkflowState.CustomData.Select(x => new Model.WorkflowStateData {Key = x.Key}),
new WorkflowStateDataEqualityComparer())
.ToList();
Now you can remove the object(s) from the original collection without removing it from the list.
You should read about LINQ's deferred execution / eager evaluation.
Answer is pretty simple, but not obvious because of use of that traitorous "var" keyword. Except returns IEnumerable, not ICollection. IEnumerable could be imagined as view to specific collection, it is not collection on its own. So when item was removed from original collection, my enumerable was changed too.

Add a List<object> or Arraylist to an array of CustomObject[]

I have tried many ways like
Cast<CustomObject>, as Customobject and ToArray(Customobject) but nothing worked.
How can I add List or ArrayList via AddRange to a CustomObject[] Array?
Code is really difficult.
But if you have some time you can get the complete source of the destination list from here:
http://www.codeproject.com/Articles/4012/C-List-View-v1-3?msg=3844172#xx3844172xx
This is a Custom Listview
I activated a combobox for the second column, so I can select diferrent values for a cell.
But before this, I have to add something to select.
This is the hole problem.
Update:
Firstly, thanks for the help !
Secondly, Found a solution in the comments from the website with the source.
Had to add some code and changed the destination custom array to a List
list.Cast<CustomObject>().ToArray()
Will work as long as the things in the list are actually CustomObject. If they might be other types, you can use OfType<CustomObject>() instead of Cast. This will filter out anything of an incompatible type.
Assuming the objects really are instances of CustomObject, use LINQ Select method:
objList.Select(o => o as CustomObject).ToArray();
Otherwise you will get an array of null.
If its a List<CustomObject> then let us say
CustomObject[] coarr = list_of_customobject.ToArray();
If its an ArrayList then
CustomObject[] coarr = arraylist.OfType<CustomObject>().ToArray();
If you are unsure whether all of your objects are of the type CustomObject try
var result = list.OfType<CustomObject>.ToArray();
Strictly speaking you cannot add elements to an array, since an array's length remains constant over its lifetime. There are two things you can do:
Create a new array
myArray = myTList.ToArray() // generic)
myArray = myArrayList.Cast<CustomObject>().ToArray() // cast, non-generic
myArray = myArrayList.OfType<CustomObject>().ToArray() // filter by type, non-generic
Set elements of an array
myArray[x] = myTList[y] // generic
myArray[x] = (CustomObject)myArrayList[y] // non-generic
I recommend you to take the generic collection whenever possible. They provide you additional type safety. Casting object variables cause runtime errors you could detect at compile time by using generic types.
If you actually want to add elements to an existing collection, you may try to use a dynamic collection type rather than an array: List<T> : IList<T> or LinkedList<T> : ICollection<T> are a good point to start, or maybe more specific types like Stack<T> or Queue<T>.

c# collection.RemoveAll(collection.OfType<type>());

Can I do something like this:
collection.RemoveAll(collection.OfType<type>());
To remove all the elements of a given type from a collection?
Both of the already-submitted answers are correct, but omit an explanation why the poster's sample code doesn't work. A couple of interesting points:
First, RemoveAll() is not defined in the ICollection or ICollection<T> interface; it's defined on List<T>, for example, but the semantically equivalent method on HashSet<T> is called RemoveWhere(). If you want to be able to do this for any ICollection<T>, you should write an extension method.
Second, the question's sample code passes a sequence of items to be removed from the collection, but List<T>.RemoveAll() and HashSet<T>.RemoveWhere() take a predicate to identify the items to be removed (as shown in the other answers). You could write your extension method to take the other approach, and pass an IEnumerable<T> as in your example. You need to be careful, though, because you can't do this:
foreach (var item in collection)
if (ShouldRemove(item))
collection.Remove(item);
If you try to do that, you should get an InvalidOperationException with a message like "Collection was modified; enumeration operation may not execute."
As the first two code answers use a List specifically, and the third only has nonworking code:
ICollection collection = GetCollection();
foreach (object obj in collection.Where(obj => obj is type).ToList())
collection.Remove(obj);
You can end the enumeration by calling ToList, so that removal is allowed again. It's totally inefficient tho, as it requires one enumeration to get the objects and then removes them one by one, possibly requiring enumeration each time. If the collection type you're using has it's own methods, like List.RemoveAll, use those instead, but your use of "collection" implies you do not know its type.
Alternatively, if the importance is not on preserving the object, consider reassigning instead:
collection = collection.Where(obj => obj is type == false).ToList();
I would do something like this:
collection.RemoveAll(i => collection.OfType().Contains(i));
EDIT:
collection.RemoveAll(i => i is type);
If I understand it correctly, you have a single collection of any object type and you want to remove all items of a type from that collection. If so, it's simple:
objects.RemoveAll(q=>q.GetType()==typeof(YourType));
collection.Clear() removes all elements.

lambda expressions on populated lists

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.

Categories