33

I have this class:

public class StatInfo
{
  public string contact;
  public DateTime date;
  public string action;
}

then I have a list of StatInfo, but I'm not sure how to sort it according to the date field. Should I use the sort method? Should I create my own?

var _allStatInfo = new List<StatInfo>();
// adding lots of stuff in it
_allStatInfo.SortByDate???

What is the best way of doing this without having to write tons of code (if possible)?

Thanks

3
  • Wait for Jon Skeet to show up. His C# in Depth: What you need to master C# 2 and 3 has a big section on just that. Commented Aug 19, 2009 at 18:37
  • Too late! I'm always surprised when one of my questions on C# doesn't get an answer from Jon Skeet :) Commented Aug 19, 2009 at 18:44
  • 2
    title is wrong. says "array" Commented May 29, 2015 at 19:29

6 Answers 6

82

Using LINQ:

var sortedList = _allStatInfo.OrderBy(si => si.date).ToList();

Sorting the original list:

_allStatInfo.Sort(new Comparison<StatInfo>((x, y) => DateTime.Compare(x.date, y.date)));
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, the second method worked. I don't use LINQ so I can't test the second one.
17

I see you've got the answer anyway, but...

  1. You can avoid some ugliness by just splitting the statement into two halves:

    Comparison<StatInfo> comparison = (x, y) => DateTime.Compare(x.date, y.date);
    _allStatInfo.Sort(comparison);
    

    You might want to consider just calling CompareTo directly, too:

    Comparison<StatInfo> comparison = (x, y) => x.date.CompareTo(y.date);
    _allStatInfo.Sort(comparison);
    
  2. You could create an IComparer<T> implementation using my ProjectionComparer class - it's part of MiscUtil, and I've included an uncommented version at the bottom of this answer. You'd then write:

    _allStatInfo.Sort(ProjectionComparer<StatInfo>.Create(x => x.date));
    
  3. Even if you're using .NET 2.0, you can still use LINQ by way of LINQBridge.

Here's the ProjectionComparer class required for the second answer. The first couple of classes are really just helpers to let generic type inference work better.

public static class ProjectionComparer
{
    public static ProjectionComparer<TSource, TKey> Create<TSource, TKey>
        (Func<TSource, TKey> projection)
    {
        return new ProjectionComparer<TSource, TKey>(projection);
    }

    public static ProjectionComparer<TSource, TKey> Create<TSource, TKey>
        (TSource ignored, Func<TSource, TKey> projection)
    {
        return new ProjectionComparer<TSource, TKey>(projection);
    }

}

public static class ProjectionComparer<TSource>
{
    public static ProjectionComparer<TSource, TKey> Create<TKey>
        (Func<TSource, TKey> projection)
    {
        return new ProjectionComparer<TSource, TKey>(projection);
    }
}

public class ProjectionComparer<TSource, TKey> : IComparer<TSource>
{
    readonly Func<TSource, TKey> projection;
    readonly IComparer<TKey> comparer;

    public ProjectionComparer(Func<TSource, TKey> projection)
        : this (projection, null)
    {
    }

    public ProjectionComparer(Func<TSource, TKey> projection,
                              IComparer<TKey> comparer)
    {
        projection.ThrowIfNull("projection");
        this.comparer = comparer ?? Comparer<TKey>.Default;
        this.projection = projection;
    }

    public int Compare(TSource x, TSource y)
    {
        // Don't want to project from nullity
        if (x==null && y==null)
        {
            return 0;
        }
        if (x==null)
        {
            return -1;
        }
        if (y==null)
        {
            return 1;
        }
        return comparer.Compare(projection(x), projection(y));
    }
}

16 Comments

@Ben: I just don't like that many brackets if I can help it :)
Jon, in your Compare method, are the null checks really needed? That should be left to caller.
@nawfal: I don't so. You want to be able to order by foo => foo.Name without having to do foo => foo == null ? null : foo.Name IMO.
@JonSkeet The example you provided could be written better, but that alone is not the case. What if we provide a method like Compute(int, string) as the projection, like _allStatInfo.Sort(ProjectionComparer<StatInfo>.Create(x => Compute(someInt, x))) where a null for x is perfectly acceptable? Throwing a null reference exception or ignoring a null value should be at the discretion of the projection if this has to be fully generic..
@nawfal: You could always create an overload for that, should you wish to. But I think it's useful not to have to explicitly handle null for the common case. Note that this is also the approach that Comparer<T>.Default takes.
|
6

To illustrate Robert C. Cartaino's answer:

public class StatInfo : IComparable<StatInfo>
{
    public string contact;
    public DateTime date;
    public string action;

    public int CompareTo(StatInfo value)
    {
        return this.date.CompareTo(value.date);
    }
}

var _allStatInfo = new List<StatInfo>();

// this now sorts by date
_allStatInfo.Sort();

Not the most general solution but good if you're only going to sort by date. And, as Robert said, you can still always override the default sort by passing an IComparer parameter to the sort method.

2 Comments

thanks. It does not apply really to my situation since I'll only sort by date in this particular situation, but I'll keep this in mind for later
@marcgg: The code in my answer makes sorting by date the default behavior. So if you are only sorting by date, I think that is exactly what you want.
4

Use a lambda expression to map a pair to a comparison:

_allStatInfo.Sort((x, y) => x.date - y.date);

1 Comment

Thanks for the answer but I get an error: Cannot convert lambda expression to type System.Collections.Generic.IComparer<AdminTool.app.models.StatInfo>' because it is not a delegate type
2

it worked for me ُSorting array of custom type using delegate

// sort array by name
Array.Sort(users, delegate(User user1, User user2) 
           {
             return user1.Name.CompareTo(user2.Name);
           });
// write array (output: Betty23 Lisa25 Susan20)
foreach (User user in users) Console.Write(user.Name + user.Age + " ");

Comments

1

For a DateTime there shouldn't be a need to compare.

_allStatInfo.OrderyBy(d => d.date);

or

_allStatInfo.OrderByDescending(d => d.Date);

3 Comments

Both methods return the list back, so you would need _allStatInfo = _allStatInfo.OrderBy(d => d.date).ToList();
Thanks, but a List<> has no such functions if I'm not mistaking
I guess this is LINQ. I'm not using LINQ

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.