List's Sort Using Lambda? - c#

What I'm trying to do is iterate over the country collection in retVal and sort each of the <StateProvinces> by Name. The hierarchy is as follows:
retVal[index].StateProvinces.ToList()[index].Name = "";
so that it is country object -> collection of states, each state has a name property and lazy loading it throwing state objects into an order based on the stateID.
What's wrong with this line of code (and why although I'm guessing I'm attempting to abuse the sort method)?
retVal[0].StateProvinces.ToList().Sort(x=>x.Name);
retVal is of type IList<Country>
Error is given for x=>x.Name:
Delegate 'System.Comparison<Country>' does not take 1 arguments

The Comparison delegate is a delegate that represents a comparison, not a projection. That is to say it takes two objects, and a result indicating which one is "smaller". You're trying to provide a projection, or a transformation of one object to another.
To write a comparison for two of these objects based on Name you'd need to write something like:
(a,b) => string.Compare(a.Name, b.Name);
You can also write a separate method to sort lists that uses a projection instead of a comparer, so that you don't need to do this for every call to Sort:
//TODO come up with a better name, but it still has to be different from `Sort`
public static void ProjectionSort<T, TKey>(
this List<T> list,
Func<T, TKey> selector,
Comparer<TKey> comparer)
{
comparer = comparer ?? Comparer<TKey>.Default;
list.Sort((a, b) => comparer.Compare(selector(a), selector(b)));
}

As error says, Comparison<T> takes two argument not one, you want something like this:
retVal[0].StateProvinces.ToList().Sort((x,y) => x.Name.CompareTo(y.Name));

Ok.. I got very frustrated with this issue last night and this morning realized the real issue was that .Sort isn't there. The method you need to call to "Sort" your collections is .OrderBy.
Hopefully this helps someone to not pull out all their hair.

Related

ASP.NET Core using Func as a parameter in a method to do orderby and thenby

I'm floundering with this. A friend wrote this sometime ago and is now in another state so cannot help with expanding the idea.
// For index listing.
// Order function to identify sort direction.
private IOrderedEnumerable<Client> OrderClients<T>(IEnumerable<Client> clients, Func<Client, T> keySelector, SortDirection sortDirection) {
return sortDirection == SortDirection.desc
? clients.OrderByDescending(keySelector)
: clients.OrderBy(keySelector);
}
It works.
However my requirements have changed.
I now want a static method that can be supplied with an IEnumerable<T> and a dictionary of <string, SortDirection> where SortDirection is the direction as an enum:
public enum SortDirection
{
asc = 0,
desc
}
This is because the Dictionary may have as many as 4 columns to sort by. The first value is always the orderBy and all the others are thenBy.
so I tried to start with the following method signature.
public static IOrderedEnumerable<T> OrderAndThenBy(IEnumerable<T> list, Func<T, Dictionary<string, SortDirection>>){}
I wanted to accept an IEnumerable and then use the dictionary with the columns to sort by "string" and the direction it should be sorted in.
This answer by Matthias seemed promising but at the heart of this I don't understand the mechanics of how the Func works as a parameter in order to use the dictionary of <key = columns, value = sortDirection> on the IEnumerable<T> (generic so I can use it on all tables that I require sorting) and then return an IOrderedEnumerable<T>..
Could someone help with the structure of the method and also how to use the Func as a method parameter with one parameter of type Dictionary. At the moment I am getting an error on the "T"..
Error CS0246 The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
because I have not written the generic method parameters correctly.. Hope someone might be able to help.
You get the error because you need to include the generic type T in the method declaration after the name (see [1]), like that:
public static IOrderedEnumerable<T> OrderAndThenBy<T>(IEnumerable<T> list, Func<T, Dictionary<string, SortDirection>> sortInfo){}
It is much more difficult to provide the info which property to sort by (key) to the OrderBy method as string like you are trying, so you better keep the key selection like in the initial OrderClients method. There, the keySelector is a delegate [3, 2] which takes a Client, and returns a property of the Client, so that the OrderBy methods know which property you want to sort by.
Now that you have multiple properties, which all could have different types, it is not sufficient to provide one type parameter anymore. But as you also do not know the number of properties that you want to sort, you cannot define all type parameters statically.
As not the cleanest but simple workaround you could drop the type, and change the delegate to return object instead T.
You probably still work just with Clients, so you should keep the return type from the original method.
Note that the order of elements in a dictionary is undefined [4], but order is important in this case, so you should replace it with e.g. a list [5].
Taken all together, you might come up with a method like:
public static IOrderedEnumerable<Client> OrderAndThenBy(IEnumerable<Client> list, List<KeyValuePair<Func<Broker, object>, SortDirection>> sortInfo) {}
[1] https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-methods
[2] https://learn.microsoft.com/en-us/dotnet/api/system.func-2?view=netframework-4.8
[3] https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
[4] https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2
[5] https://learn.microsoft.com/en-us/dotnet/standard/collections/selecting-a-collection-class

What difference between the Queryable.Contains method and the List<T>.Contains method in C#?

List<A> list=new List<A>();
var a=new A();
list.Add(a);
list.Contains(a);
list.Contains<A>(a);
What difference between this two cases?
Why exists two method?
There is no difference in this case between List.Contains (which is the implementation of ICollection.Contains) and Enumerable.Contains - in the case where the enumerable is a collection, IEnumerable.Contains simply invokes ICollection.Contains.
The reasoning there is that some collections - such as SortedSet - can implement a Contains method that operates at better than O(n) time. For non-collection types of Enumerable, IEnumerable.Contains will do a linear search over the enumeration.
There is also Queryable.Contains, but that's different - List isn't a queryable. Queryable.Contains can build this into a query expression which can be translated (for example, into SQL). Queryable and Enumerable extension methods are very different under the hood.
If you ask about difference in its functionality, then there is actually none.
List.Contains() is a part of ICollection interface and exists since .NET Framework 2.0. Developers have always used this method before LINQ to check if List or another ICollection contains an item.
.Contains<T> is a part of LINQ. It is a query language which allows you to use this method with all IEnumerable collections, even arrays or custom; data sources likes databases; data formats like JSON or XML etc.
Actually, when you call LINQ .Contains<T> on IEnumerable collection which is ICollection (for example, List<T>), it does call its own ICollection.Contains method.
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value)
{
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
return collection.Contains(value);
return Enumerable.Contains<TSource>(source, value, (IEqualityComparer<TSource>) null);
}
The algorithmic complexity of "LIST.contains" is O(n) always.
Where as "Queryable.contains" complexity depends on the collection implemented. For example, if underlying collection is "Hashset", then the algorithmic complexity is O(1).

Does usage of contains on IEnumerable cast it to a List?

I'm using Linq to filter Data I get from the database. Due to design choices made 1 method returns me an IEnumerable<int> which I then use for a linq statement to see which IDs are permitted to be returned (code follows below). My question here is as I'm not seeing anything there in the documentation: Does the Contains method implicitly cast the IEnumerable to a List for the statement to be executed? (If so the question is if using List in the first place instead of IEnumerable is better).
Code Example
private List<MyData> GetAllPermittedData()
{
IEnumerable<int> permitteddIds = GetPermittedIDs();
return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
select a);
}
Like I asked above I'm not sure if the Contains part implicitly converts permittedIds into a List<int> (for/inside the use of the Contains statement). If this is the case then a followup question would be if it is not better to already use the following statement instead (performance-wise):
private List<MyData> GetAllPermittedData()
{
List<int> permitteddIds = GetPermittedIDs().ToList();
return (from a in MyDataHandler.GetAllData() where permittedIds.Contains(a.Id)
select a);
}
The LINQ operator will attempt to cast it to ICollection<T> first. If the cast succeeds, it uses that method. Since List<T> implements this interface, it will use the list's contain method.
Note that if you use the overload that accepts an IEqualityComparer, it must iterate over the enumerable and the ICollection shortcut is not taken.
You can see this implementation in the .NET Framework reference source:
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value) {
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null) return collection.Contains(value);
return Contains<TSource>(source, value, null);
}
Jon Skeet also has a good (and lengthy) blog series called "Reimplementing LINQ" where he discusses the implementation in depth. He specifically covers Contains in part 32 of his blog.
The Contains method may try to cast the passed IEnumerable<T> to IList<T> or to ICollection<T>. If the cast succeeds, it may directly use the methods of IList<T>, otherwise it will enumerate over the full sequence.
Note that I am writing may because this is implementation-specific and it is not specified in the docs. As such, it could be different across .NET versions and also in alternative implementations such as Mono.
Your advantage by providing only an IEnumerable<T> is that you have more freedom to exchange the object returned from that property without changing the public interface. The performance cost of the attempted cast to IList<T> or similar should be negligible.
In any case, this way is more performant than your suggestion of calling ToList, as that will actually create a new List<T> and copy all items from the enumeration into it.
Contains exists as an extension method for IEnumerable<T>. But you con't need to convert your IEnumerable to a List<T> with ToList(), you could simply use that IEnumerable<T> to fill a HashSet<T>:
var permitteddIds = new HashSet<int>(GetPermittedIDs());

List<T> Ordering

I have an issue, I am allowing a user to select the criterea for ordering a List
Lets say my list is called
List<Cars> AllCars = new List<Cars>;
allCars = //call the database and get all the cars
I now want to order this list
allCars.orderBy(registrationDate)
I understand the above doesn't work but i haven't anyidea what i should be putting in the brackets.
allCars.OrderBy(c => c.RegistrationDate);
I understand the above doesn't work but i haven't anyidea what i should be putting in the brackets.
The declaration of Enumerable.OrderBy is
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
and, as it's an extension method it can be invoked as
source.OrderBy(keySelector).
Your List<Car> is playing the role of source as List<T> : IEnumerable<T>. The second parameter is the more interesting one and the one that you are confused by. It's declared as being of type
Func<TSource, TKey>
This means that it is a delegate that eats instances of TSource (in your case Car) and returns an instance of TKey; it's up to you to decide what TKey is. You have stated that you want to order by Car.registrationDate so it sounds like TKey is DateTime. Now, how do we get one of these delegates?
In the old days we could say
DateTime GetRegistrationDate(Car car) {
return car.registrationDate;
}
and use OrderBy like so:
allCars.OrderBy(GetRegistrationDate).
In C# 2.0 we gained the ability to use anonymous delegates; these are delegates that don't have a name and are defined in-place.
allCars.OrderBy(delegate(Car car) { return car.registrationDate; });
Then, in C# 3.0 we gained the ability to use lambda expressions which are very special anonymous delegates with a compact notation
allCars.OrderBy(car => car.registrationDate);
Here, c => c.registrationDate is the lambda expression and it represents a Func<Car, DateTime> than can be used the second parameter in Enumerable.OrderBy.
allCars.orderBy(registrationDate)
The reason this doesn't work is because registrationDate is not a delegate. In fact, without any context at all registrationDate is meaningless to the compiler. It doesn't know if you mean Car.registrationDate or maybe you mean ConferenceAttendee.registrationDate or who knows what. This is why you must give additional context to the compiler and tell it that you want the property Car.registrationDate. To do this, you use a delegate in one of the three ways mentioned above.

list sorting in c#

i have list of objects i need to sort the list based on object's property1 and i need to sort again the resultant list with object's property2 without loosing the grouping done in first sorting ....
for ex:
obj has 2 property name and location
i need to have a final list of objects which has been sorted with region and objects of same region should be sorted by name...
(Assuming you don't have LINQ available to you, which makes this trivial.)
If you look in MiscUtil, you'll find two useful classes: ProjectionComparer and LinkedComparer (IIRC).
ProjectionComparer basically implements the LINQ "order by" concept - you specify how to convert a source element to a key value, and the comparer will order by those key values.
LinkedComparer takes two comparers and returns a new comparer which uses the "primary" comparer first, and the "secondary" comparer if values are equal with respect to the primary one.
Create two projection comparers (one for each property) and then a linked comparer with the two of them, then pass that to List<T>.Sort. Let me know if you need a full code sample, but it would be something like (using C# 3):
var comparer = new LinkedComparer<Foo>
(ProjectionComparer<Foo>.Create(x => x.FirstProperty),
ProjectionComparer<Foo>.Create(x => x.SecondProperty));
(In C# 2 you could use anonymous methods, they'd just be a bit more long-winded.)
Sounds like you want to use LINQ's orderby and thenby syntax.
A List has a Sort method which takes a Comparision delegate as an argument.
There are also overloads where you can pass in your own comparer.
So, you can write a class which implements IComparer. Then, in the implementation of this class, you write the code where you compare the 2 objects on the properties you want.

Categories

Resources