2

I've tried looking for an existing question but wasn't sure how to phrase this and this retrieved no results anywhere :(

Anyway, I have a class of "Order Items" that has different properties. These order items are for clothing, so they will have a size (string).

Because I am OCD about these sorts of things, I would like to have the elements sorted not by the sizes as alphanumeric values, but by the sizes in a custom order.

I would also like to not have this custom order hard-coded if possible.

To break it down, if I have a list of these order items with a size in each one, like so:

2XL
S
5XL
M

With alphanumeric sorting it would be in this order:

2XL
5XL
M
S

But I would like to sort this list into this order (from smallest size to largest):

S
M
2XL
5XL

The only way I can think of to do this is to have a hard-coded array of the sizes and to sort by their index, then when I need to grab the size value I can grab the size order array[i] value. But, as I said, I would prefer this order not to be hard-coded.

The reason I would like the order to be dynamic is the order items are loaded from files on the hard disk at runtime, and also added/edited/deleted by the user at run-time, and they may contain a size that I haven't hard-coded, for example I could hard code all the way from 10XS to 10XL but if someone adds the size "110cm" (aka a Medium), it will turn up somewhere in the order that I don't want it to, assuming the program doesn't crash and burn.

I can't quite wrap my head around how to do this.

4
  • 1
    When the items are loaded where are they loaded? Into a DB? couldn't you apply an order index here? Commented Mar 5, 2013 at 13:10
  • I just answered a similar question, here's my answer: stackoverflow.com/questions/15223213/… Commented Mar 5, 2013 at 13:11
  • 1
    If someone adds 110cm, how do you know its meaning? Commented Mar 5, 2013 at 13:12
  • Tim: Therein lies the problem. I fear I might just have to remove the custom size input and just give the user a list, and hard code the order. Liam: The items are just loaded into an Order class which is a custom class that inherits from List<OrderItem>, to which I've added a few methods for retrieving items which match certain fields. Nolonar: Thanks, I'll take a look now. Commented Mar 5, 2013 at 13:17

7 Answers 7

6

Also, you could create a Dictionary<int, string> and add Key as Ordering order below. Leaving some gaps between Keys to accomodate new sizes for the future. Ex: if you want to add L (Large), you could add a new item as {15, "L"} without breaking the current order.

 Dictionary<int, string> mySizes = new Dictionary<int, string> { 
                                       { 20, "2XL" }, { 1, "S" },
                                       { 30, "5XL" }, { 10, "M" }
                                    };

 var sizes = mySizes.OrderBy(s => s.Key)
                    .Select(s => new {Size =  s.Value})
                    .ToList();
Sign up to request clarification or add additional context in comments.

2 Comments

@CliftonJKent i would still wrap this up in an IComparer<string> so that its reusable.
@DanielA.White Yes, that makes sense as well. Perhaps I could try parsing each size to an integer value, and using that as the key.
6

You can use OrderByDescending + ThenByDescending directly:

sizes.OrderByDescending(s => s == "S")
     .ThenByDescending( s => s == "M")
     .ThenByDescending( s => s == "2XL")
     .ThenByDescending( s => s == "5XL")
     .ThenBy(s => s);

I use ...Descending since a true is similar to 1 whereas a false is 0.

5 Comments

this would have a lot of overhead.
Doesn't this "hard code" the order? Which the OP is trying to avoid?
I assume that this is what you meant with hardcoded. @DanielA.White: What's the overhead? That's similar to an if-else or in sql ORDER BY a DESC, B DESC, C DESC, ...
its looping through several times. not to mention the internal looping of the string equality.
@DanielA.White: I think you have misunderstood how OrderBy...ThenBy works. They don't need to reorder all elements all the time. ThenBy only is consulted if OrderBy returns two equal elements. stackoverflow.com/a/10827971/284240
3

I would implement IComparer<string> into your own TShirtSizeComparer. You might have to do some regular expressions to get at the values you need.

IComparer<T> is a great interface for any sorting mechanism. A lot of built-in stuff in the .NET framework uses it. It makes the sorting reusable.

I would really suggest parsing the size string into a separate object that has the size number and the size size then sorting with that.

Comments

1

You need to implement the IComparer interface on your class. You can google how to do that as there are many examples out there

2 Comments

I've looked at IComparer, would this not be a hard coded solution, though?
@CliftonJKent there is some hard coding with Icomparer, but you could put the logic in a decision table.
0

you'll have to make a simple parser for this. You can search inside the string for elements like XS XL and cm" if you then filter that out you have your unit. Then you can obtain the integer that is the value. If you have that you can indeed use an IComparer object but it doesn't have that much of an advantage.

Comments

0

I would make a class out of Size, it is likely that you will need to add more functionality to this in the future. I added the full name of the size, but you could also add variables like width and length, and converters for inches or cm.

private void LoadSizes()
{
    List<Size> sizes = new List<Size>();
    sizes.Add(new Size("2X-Large", "2XL", 3));
    sizes.Add(new Size("Small", "S", 1));
    sizes.Add(new Size("5X-Large", "5XL", 4));
    sizes.Add(new Size("Medium", "M", 2));

    List<string> sizesShortNameOrder = sizes.OrderBy(s => s.Order).Select(s => s.ShortName).ToList();
    //If you want to use the size class:
    //List<Size> sizesOrder = sizes.OrderBy(s => s.Order).ToList();
}

public class Size
{
    private string _name;
    private string _shortName;
    private int _order;

    public string Name
    {
        get { return _name; }
    }

    public string ShortName
    {
        get { return _shortName; }
    }

    public int Order
    {
        get { return _order; }
    }

    public Size(string name, string shortName, int order)
    {
        _name = name;
        _shortName = shortName;
        _order = order;
    }
}

Comments

0

I implemented TShirtSizeComparer with base class Comparer<object>. Of course you have to adjust it to the sizes and objects you have available:

public class TShirtSizeComparer : Comparer<object>
{
    // Compares TShirtSizes and orders them by size
    public override int Compare(object x, object y)
    {
        var _sizesInOrder = new List<string> { "None", "XS", "S", "M", "L", "XL", "XXL", "XXXL", "110 cl", "120 cl", "130 cl", "140 cl", "150 cl" };
        var indexX = -9999;
        var indexY = -9999;
        if (x is TShirt)
        {
            indexX = _sizesInOrder.IndexOf(((TShirt)x).Size);
            indexY = _sizesInOrder.IndexOf(((TShirt)y).Size);
        }
        else if (x is TShirtListViewModel)
        {
            indexX = _sizesInOrder.IndexOf(((TShirtListViewModel)x).Size);
            indexY = _sizesInOrder.IndexOf(((TShirtListViewModel)y).Size);
        }
        else if (x is MySelectItem)
        {
            indexX = _sizesInOrder.IndexOf(((MySelectItem)x).Value);
            indexY = _sizesInOrder.IndexOf(((MySelectItem)y).Value);
        }
        if (indexX > -1 && indexY > -1)
        {
            return indexX.CompareTo(indexY);
        }
        else if (indexX > -1)
        {
            return -1;
        }
        else if (indexY > -1)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

To use it you just have a List or whatever your object is and do:

tshirtList.Sort(new TShirtSizeComparer());

The order you have "hard-coded" is prioritized and the rest is put to the back.

I'm sure it can be done a bit smarter and more generalized to avoid hard-coding it all. You could e.g. look for sizes ending with an "S" and then check how many X's (e.g. XXS) or the number before X (e.g. 2XS) and sort by that, and then repeat for "L" and perhaps other "main sizes".

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.