C# how to sort a list without implementing IComparable manually?
I have a fairly complex scenario and I need to ensure items I have in a list are sorted.
Firstly the items in the list are based on a struct that contains a sub struct.
For example:
public struct topLevelItem
{
public custStruct subLevelItem;
}
public struct custStruct
{
public string DeliveryTime;
}
Now I have a list comprised of topLevelItems for example:
var items = new List<topLevelItem>();
I need a way to sort on the DeliveryTime ASC. What also adds to the complexity is that the DeliveryTime field 开发者_开发知识库is a string. Since these structs are part of a reusable API, I can't modify that field to a DateTime, neither can I implement IComparable in the topLevelItem class.
Any ideas how this can be done?
Thank you
Create a new type that implements IComparer and use an instance of it to compare the objects.
public class topLevelItemComparer : IComparer<topLevelItem>
{
public int Compare(topLevelItem a, topLevelItem b)
{
// Compare and return here.
}
}
You can then call Sort() like this:
var items = new List<topLevelItem>();
// Fill the List
items.Sort(new topLevelItemComparer());
It sounds like you need to get canonicalized date sorting even though your date is represented as a string, yes? Well, you can use LINQ's OrderBy
operator, but you will have to parse the string into a date to achieve correct results:
items = items.OrderBy(item => DateTime.Parse(item.subLevelItem.DeliveryTime))
.ToList();
Update:
I've added this in for completeness - a real example of how I use ParseExact with Invariant culture:
var returnMessagesSorted = returnMessages.OrderBy((item => DateTime.ParseExact(item.EnvelopeInfo.DeliveryTime, ISDSFunctions.GetSolutionDateTimeFormat(), CultureInfo.InvariantCulture)));
return returnMessagesSorted.ToList();
You can always implement a separate IComparer class, it's not fun, but it works well:
public class TopLevelItemComparer : IComparer<topLevelItem>
{
public int Compare( topLevelItem x, topLevelItem y )
{
return DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
DateTime.Parse(y.subLevelItem.DeliveryTime) );
}
}
items.Sort( new TopLevelItemComparer() );
Be aware that most Sort()
methods in the .NET framework accept an IComparer
or IComparer<T>
which allows you to redefine the comparison semantics for any type. Normally, you just use Comparer<T>.Default
- or use an overload that essentially supplies this for you.
Using LINQ:
items = items.OrderBy(item => item.subLevelItem.DeliveryTime).ToList();
If you want to perform an in-place sort then you can use the Sort
overload that takes a Comparison<T>
argument and pass an anonymous function/lambda:
items.Sort((x, y) => DateTime.Parse(x.subLevelItem.DeliveryTime).CompareTo(
DateTime.Parse(y.subLevelItem.DeliveryTime)));
If you prefer to create a new sorted sequence rather than an in-place sort then LINQ's OrderBy
is probably the way to go, as others have already mentioned.
Having had this problem before I once implemented a LambdaComparer that did the compare based on an arbitrary lambda expression. Not exact code but something along these lines:
public class LambdaComparer : IComparer<T>
{
private Func<T,T,int> _func;
public LambdaComparer(Func<T,T,int> function)
{
_func = function;
}
public int Compare(T x, T y)
{
return _func(x,y);
}
}
Big advantage of this is you get a nice reusable chunk of code.
To sort the items
list itself:
Comparison<topLevelItem> itemComparison = (x, y) => {
DateTime dx;
DateTime dy;
bool xParsed = DateTime.TryParse(x.subLevelItem.DeliveryTime, out dx);
bool yParsed = DateTime.TryParse(y.subLevelItem.DeliveryTime, out dy);
if (xParsed && yParsed)
return dx.CompareTo(dy);
else if (xParsed)
return -1; // or 1, if you want invalid strings to come first
else if (yParsed)
return 1; // or -1, if you want invalid strings to come first
else
// simple string comparison
return x.subLevelItem.DeliveryTime.CompareTo(y.subLevelItem.DeliveryTime);
};
items.Sort(itemComparison);
This approach has the advantage of:
- Sorting the list in place (that is, if you actualy want the list sorted in-place)
- Sorting by actual
DateTime
values, rather than strings, BUT... - Not throwing an exception if a string does not represent a valid
DateTime
(basically, all the invalid strings will end up on one side of the list)
精彩评论