Alphabetically sort a generic list of objects using a specified property
Asked Answered
D

3

5

I am writing an address book program. I have each person's details stored in a List<Person>. I need to be able to sort this list by last name (using first name if there are ties) or by post code.

So far I have this:

public class Person
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string PostCode { get; set; }
    // etc..
}

public class AddressBook
{
    public List<Person> People { get; set; }

    // asc: ascending or descending
    // column: the property to use when sorting
    //         (in my case either LastName or Postcode)
    public void Sort(bool asc, string column)
    {
        // What should I put here?
    }

    // etc...
}

I have tried using the ICompare and IComparable interfaces but I am just not getting it.

How do I write the Sort method?

Defray answered 21/3, 2012 at 11:34 Comment(2)
What version of C# / .NET are you using?Segalman
I tried to clean up your question so that it's easier to read. I hope it's OK.Segalman
P
3

You can use an implementation of IComparer<T>:

public class PersonComparer : IComparer<Person>
{
    private readonly bool _sortAscending;
    private readonly string _columnToSortOn;

    public PersonComparer(bool sortAscending, string columnToSortOn)
    {
        _sortAscending = sortAscending;
        _columnToSortOn = columnToSortOn;
    }

    public int Compare(Person x, Person y)
    {
        if(x == null && y == null) return 0;
        if(x == null) return ApplySortDirection(-1);
        if(y == null) return ApplySortDirection(1);

        switch(_columnToSortOn)
        {
            case "LastName":
                return ApplySortDirection(SortByName(x, y));
                break;
            case "PostCode":
                return ApplySortDirection(SortByPostCode(x, y));
                break;
            default:
                throw new ArgumentOutOfRangeException(
                    string.Format("Can't sort on column {0}",
                    _columnToSortOn));
        }
    }

    private int SortByPostCode(Person x, Person y)
    {
        return x.PostCode.CompareTo(y.PostCode);
    }

    private int SortByName(Person x, Person y)
    {
        var lastNameResult = x.LastName.CompareTo(y.LastName);
        if(lastNameResult != 0)
            return lastNameResult;
        return x.FirstName.CompareTo(y.FirstName);
    }

    private int ApplySortDirection(int result)
    {
        return _sortAscending ? result : (result * -1);
    }
}

You would use it in the Sort method of your AddressBook class like this, assuming People is a List<Person>:

public void Sort(bool asc, string column)
{
    People.Sort(new PersonComparer(asc, column));
}

This code has the benefit of using an in-place sort.

Pitts answered 21/3, 2012 at 11:47 Comment(1)
Works perfectly. AND... (even better) I understand it... Thank you very much!Defray
A
8

Assuming:

 List<Person> personList;

then with Linq:

 IEnumerable<Person> orderedByLastName = personList.OrderBy(p => p.LastName)
Aynat answered 21/3, 2012 at 11:36 Comment(2)
Thanks Spender, I am clearly missing something (need to do a bit more reading into linq) as this is not working for me at all. When I run the tests through the sort method the List is not sorted at all in anyway.Defray
@user1243505: That's because this - and Mark's answer too - don't do in-place sorting. They return a new object which contains the content's of the original list in the sorted order.Pitts
S
5

You can try using the LINQ extension methods OrderBy, OrderByDescending, ThenBy and ThenByDescending:

using System.Linq;

// ...

public void Sort(bool asc, string column)
{
    switch (column)
    {
        case "LastName":
            People = People.OrderBy(x => x.LastName).ThenBy(x => x.FirstName).ToList();
            break;
        case "PostCode":
            People = People.OrderBy(x => x.PostCode).ToList();
            break;
        default:
            // error handling
    }

    if (!asc)
    {
        People.Reverse();
    }
}

You could also look at Dynamic LINQ which would simplify this code.

Segalman answered 21/3, 2012 at 11:35 Comment(2)
Thanks for your reply. As I said below, I need to look into linq a lot more as i dont understand how to get your code to sort the list. Thank you for your help though.Defray
@user1243505: If I were you I'd invest in my future and learn LINQ. It's much simpler and easier to use. A small investment in your own education now will save you a lot of time in the long run, and make you more a valuable programmer in the future. But it's your life, your career, your choice...Segalman
P
3

You can use an implementation of IComparer<T>:

public class PersonComparer : IComparer<Person>
{
    private readonly bool _sortAscending;
    private readonly string _columnToSortOn;

    public PersonComparer(bool sortAscending, string columnToSortOn)
    {
        _sortAscending = sortAscending;
        _columnToSortOn = columnToSortOn;
    }

    public int Compare(Person x, Person y)
    {
        if(x == null && y == null) return 0;
        if(x == null) return ApplySortDirection(-1);
        if(y == null) return ApplySortDirection(1);

        switch(_columnToSortOn)
        {
            case "LastName":
                return ApplySortDirection(SortByName(x, y));
                break;
            case "PostCode":
                return ApplySortDirection(SortByPostCode(x, y));
                break;
            default:
                throw new ArgumentOutOfRangeException(
                    string.Format("Can't sort on column {0}",
                    _columnToSortOn));
        }
    }

    private int SortByPostCode(Person x, Person y)
    {
        return x.PostCode.CompareTo(y.PostCode);
    }

    private int SortByName(Person x, Person y)
    {
        var lastNameResult = x.LastName.CompareTo(y.LastName);
        if(lastNameResult != 0)
            return lastNameResult;
        return x.FirstName.CompareTo(y.FirstName);
    }

    private int ApplySortDirection(int result)
    {
        return _sortAscending ? result : (result * -1);
    }
}

You would use it in the Sort method of your AddressBook class like this, assuming People is a List<Person>:

public void Sort(bool asc, string column)
{
    People.Sort(new PersonComparer(asc, column));
}

This code has the benefit of using an in-place sort.

Pitts answered 21/3, 2012 at 11:47 Comment(1)
Works perfectly. AND... (even better) I understand it... Thank you very much!Defray

© 2022 - 2024 — McMap. All rights reserved.