0

I have that class

public class MyItem
{
    public Order OrderItem { get; set; }
    public string Status { get; set; }
}

the Status property can have values Error, Pending, Success

I want to sort the List<MyItem>() object basing on the Status property.

I know that I can replace the type of the Status property from string to int and then instead of Error, Pending, Success values I can have 1,2,3 and then the sort operation is simple. But I'm wondering how to do it if we must do the sorting on basing on the string type property.

Possible order:

Error, Pending, Success or Success, Pending, Error

how to do it by using LINQ ?

2
  • 8
    Why strings and not enums? Commented Jun 6, 2013 at 10:26
  • 2
    You can write a custom IComparer<string> but, like @PLB says, why not use an enum? Commented Jun 6, 2013 at 10:28

8 Answers 8

5

I would use an enum instead which by default gives each status an integer value e.g.

public enum Status
{
    Success,
    Pending,
    Error
}

var sortedList = myList.OrderBy(x => x.Status);
// sort by Success, Pending and then Error
var sortedList = myList.OrderByDescending(x => x.Status);
// sort by Error, Pending, Success

You can convert your existing strings to enum pretty easily:

var status = (Status)Enum.Parse(typeof(Status), statusString);
Sign up to request clarification or add additional context in comments.

2 Comments

I've done that already but I'm wondering about the string case
@Tony if you had to compare string's you would need to write your own IComparer<T> class or make your MyItem class implement IComparable<T>. However, I would definitely push for switching to enum in this case, it's the right way to go.
4

Its better to parse to an enum but, you could make a custom comparer,

var customComparer = Comparer<string>.Create((x, y) =>
    {
        ...
    });

You can than pass the the comparer into sort and order by functions, like this,

var sortedList = myList.OrderBy(x => x.Status, customComparer);

Comments

3

The easiest way is probably to implement the IComparable<T> interface. Implementing this interface on your MyItem class will ensure that when ordering a list of MyItem instances, the ordering as specified in the CompareTo method will be used. For your use case, this would result in code like this:

public class MyItem : IComparable<MyItem>
{
    public string OrderItem { get; set; }
    public string Status { get; set; }

    public int CompareTo(MyItem other)
    {
        if (other == null)
        {
            return 1;
        }

        if (other.Status == this.Status)
        {
            return 0;
        }

        var statusAsInt = this.Status == "Error" ? 0 : (this.Status == "Pending" ? 1 : 2);
        var otherStatusAsInt = other.Status == "Error" ? 0 : (other.Status == "Pending" ? 1 : 2);

        if (statusAsInt == otherStatusAsInt)
        {
            return 0;
        }

        return statusAsInt < otherStatusAsInt ? -1 : 1;
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var myItems = new List<MyItem> { new MyItem { Status = "Pending" }, new MyItem { Status = "Error" }, new MyItem { Status = "Success " } };

        myItems.Sort();

        foreach (var myItem in myItems)
        {
            Console.WriteLine(myItem.Status);
        }    
    }
}

Comments

2

You could define a dictionary which reflects the required ordering

Dictionary<string, int> ordering = new Dictionary<string, int> {
  { "Error", 1 },
  { "Pending", 2 },
  ...
}

var sortedList = myList.OrderBy(x => ordering[x.Status]);

Comments

1

As the orders happen to be alphabetical, you can do a simple

var result = myList.OrderBy(item => item.Status);

or

var result = myList.OrderByDescending(item => item.Status);

However, if you rename "Success" as "Complete", then it'll break, in which case I'd recommend one of the suggested enum answers.

Comments

1

I think it would be better to change the type of Status and replace the String by an enum. You can then choose the order you want by assigning values to the different members of your enum, or directly order this member as desired by your order method, and keep a readable property.

Comments

0

I would go for the Enum option but given that it's not your preferred option you might try the following:

You can implement IComparable<MyItem> in your class MyItem

Over there implement your logic of ordering according to the Status property. If two status are equal you can even then order by OrderItem, if that suits you.

If you need an example feel free to say so

Comments

0

You can pass .OrderBy() a method that weights the strings using a switch like this:

     List<MyItem> lst = new List<MyItem>();

     lst.Add(new MyItem() { Status = "Pending" });
     lst.Add(new MyItem() { Status = "Error" });
     lst.Add(new MyItem() { Status = "Invalid String" });
     lst.Add(new MyItem() { Status = "Success" });

     var ascList =
        lst.OrderBy((x) => { switch (x.Status) { case "Error": return 0; case "Pending": return 1; case "Success": return 3; default: return 4; } });

     var descList =
        lst.OrderByDescending((x) => { switch (x.Status) { case "Error": return 0; case "Pending": return 1; case "Success": return 3; default: return 4; } });

     Console.WriteLine("Asc\n===");
     foreach (MyItem mi in ascList)
     {
        Console.WriteLine(mi.Status);
     }

     Console.WriteLine("\nDesc\n====");
     foreach (MyItem mi in descList)
     {
        Console.WriteLine(mi.Status);
     }
  }

Comments

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.