Ive been trying to get OrderBy
in a LINQ statement to work with an anonymous object but have failed by now.
I checked these already:
Anonymous IComparer implementation
C# linq sort - quick way of instantiating IComparer
How to sort an array of object by a specific field in C#?
I spent a few hours trying different approaches but there has to be something I'm missing.
Let's say there's the following class:
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
public int Popularity {get; set;}
public decimal Price {get; set;}
}
And products
is a list of these objects.
How can I complete this LINQ statement, so that it works with the anonymous object ?
To be clear, I know I can do this in a different way but I'd be very interested to learn how to make this particular example work.
var sortedProducts = products
.OrderBy(p =>
new {p.Popularity, p.Price},
[IComparer magic goes here]);
It seems that it should be possible with an implementation of the ProjectionComparer
:
http://code.google.com/p/edulinq/source/browse/src/Edulinq/ProjectionComparer.cs?r=0c583631b709679831c99df2646fc9adb781b2be
Any ideas how to do this ?
UPDATE:
I did a quick performance test on this - the anonymous comparer solution vs standard orderby.thenby and it seems that the anonymous solution is quite slower which is probably what we might have expected anyway.
numProd | Anon | chained orderby clauses
10 000 | 47 ms | 31 ms
100 000 | 468 ms | 234 ms
1 000 000| 5818 ms | 2387 ms
5 000 000| 29547 ms| 12105 ms
You can make an IComparer<T>
implementation that uses a delegate that you supply for the comparison, and instantiate it with type inference (similar to "cast by example"):
static class AnonymousComparer
{
public static IComparer<T> GetComparer<T>(T example, Comparison<T> comparison)
{
return new ComparerImpl<T>(comparison);
}
private class ComparerImpl<T> : IComparer<T>
{
private readonly Comparison<T> _comparison;
public ComparerImpl(Comparison<T> comparison) { _comparison = comparison; }
public int Compare(T x, T y) { return _comparison.Invoke(x, y); }
}
}
And use it thus:
var comparer = AnonymousComparer.GetComparer(
new { Popularity = 0, Price = 0m },
(a, b) => //comparison logic goes here
);
var sortedProducts = products
.OrderBy(p =>
new { p.Popularity, p.Price },
comparer);
EDIT: I just checked out the projection comparer page you linked to. With that approach, you don't need the "example" argument for type inference. The approach still needs to be adapted, however, to take a delegate instead of an interface. Here it is:
//adapted from http://code.google.com/p/edulinq/source/browse/src/Edulinq/ProjectionComparer.cs?r=0c583631b709679831c99df2646fc9adb781b2be
static class AnonymousProjectionComparer
{
private class ProjectionComparer<TElement, TKey> : IComparer<TElement>
{
private readonly Func<TElement, TKey> keySelector;
private readonly Comparison<TKey> comparison;
internal ProjectionComparer(Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
{
this.keySelector = keySelector;
this.comparison = comparison ?? Comparer<TKey>.Default.Compare;
}
public int Compare(TElement x, TElement y)
{
TKey keyX = keySelector(x);
TKey keyY = keySelector(y);
return comparison.Invoke(keyX, keyY);
}
}
public static IComparer<TElement> GetComparer<TElement, TKey>(Func<TElement, TKey> keySelector, Comparison<TKey> comparison)
{
return new ProjectionComparer<TElement, TKey>(keySelector, comparison);
}
}
You don't really need an anonymous object to order these objects by populartiy descending and then price, you can use OrerBy and ThenBy in combination, like:
var sortedProducts = products.OrderByDescending(p => p.Popularity)
.ThenBy(p => p.Price);
To do an IComparer<T>
on an anonymous type, you'd be best off using a factory to construct one from a delegate and using type inference (specifying anonymous types without inference is a pain!).
You might want to measure the performance implications of creating anonymous objects purely for ordering, but Phoogs answer gives a good way to use Comparison<T>
delegate to construct an IComparer<T>
on the fly..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With