HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Safely order a list of objects by DateTime

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
orderobjectssafelylistdatetime

Problem

I have several lists of various objects that need to be ordered by one of their properties that contains a string representation of DateTime, whose property also varies depending on the object.

The main issue with the lists is that the contained objects cannot be guaranteed to be valid DateTime representations and so simply ordering them (using LINQ) would throw exceptions if the property is null or an invalid format.

This means I have to use something like DateTime.TryParse. Here's the class method I wrote to do this:

static List SafelyOrderListByDateTimeDescending(List list, string propertyName)
{
    DateTime value;
    PropertyInfo propInfo;
    string dateTimeString;
    foreach (T obj in list)
    {
        propInfo = obj.GetType().GetProperty(propertyName);
        dateTimeString = propInfo.GetValue(obj, null) as string;
        if (!DateTime.TryParse(dateTimeString, out value))
            propInfo.SetValue(obj, default(DateTime).ToString(), null);
    }

    return list.OrderByDescending(x => DateTime.Parse(x.GetType().GetProperty(propertyName).GetValue(x, null) as string)).ToList();
}


Considering there are many thousands of objects per list it's important that the method is not exceedingly inefficient. It seems to perform well in tests but I'd like some feedback on what could be improved or even if there's a better way.

I've tried some really neat ways of doing something similar but they all exclude the object if the value isn't successfully parsed whereas I still need the object.

Solution

You call this method SafelyOrderListByDateTimeDescending but there is nothing safe about it. I'd be really surprised if I found that a sorting function actually modified the objects I wanted to sort.

propInfo.SetValue(obj, default(DateTime).ToString(), null);


This shouldn't be there. You either need another method that sets default values for uninitialized properties so that the user knows his objects are modified or... you need another really safe approach. This means you need a special comparer that you can use with the OrderByDescending

class DateTimeComparer : IComparer
{
    private readonly PropertyInfo _property;

    public DateTimeComparer(string propertyName) => _property = typeof(T).GetProperty(propertyName);

    public int Compare(T left, T right)
    {
        return GetDateTimeOrDefault(left).CompareTo(GetDateTimeOrDefault(right));
    }

    private DateTime GetDateTimeOrDefault(T obj)
    {
        return 
            DateTime.TryParse(_property.GetValue(obj) as string, out DateTime result)
            ? result
            : default(DateTime);
    }
}


It does not modifiy anything. Internally it either uses the value provided by the property or uses the default(DateTime). The constructor requires that you specify the name of the property to compare. (It's C# 7).

Example:

var result = values.OrderByDescending(x => new DateTimeComparer("YourProperty"));

Code Snippets

propInfo.SetValue(obj, default(DateTime).ToString(), null);
class DateTimeComparer<T> : IComparer<T>
{
    private readonly PropertyInfo _property;

    public DateTimeComparer(string propertyName) => _property = typeof(T).GetProperty(propertyName);

    public int Compare(T left, T right)
    {
        return GetDateTimeOrDefault(left).CompareTo(GetDateTimeOrDefault(right));
    }

    private DateTime GetDateTimeOrDefault(T obj)
    {
        return 
            DateTime.TryParse(_property.GetValue(obj) as string, out DateTime result)
            ? result
            : default(DateTime);
    }
}
var result = values.OrderByDescending(x => new DateTimeComparer<YourType>("YourProperty"));

Context

StackExchange Code Review Q#156115, answer score: 5

Revisions (0)

No revisions yet.