0

I have these TextBoxes labeled as tb_class# and # = 1-10

So far this is the function I have

    private List<TextBox> GetCustomClasses()
    {
        List<TextBox> tb = new List<TextBox>();
        foreach (Control con in gb_customClasses.Controls)
            if (con.Name.Contains("tb_class"))
                tb.Add(con as TextBox);
        return tb.OrderByDescending(x => x.Name.Replace("tb_class", "")).ToList();
    }

The output looks like this:

9
8
7
6
5
4
3
2
10
1

I could add a check for this, but I want it to go in perfect order. For those wondering gb_customClasses is a groupbox.

Solved! Final code:

    private List<TextBox> GetCustomClasses()
    {
        List<TextBox> tb = new List<TextBox>();
        foreach (Control con in gb_customClasses.Controls)
            if (con.Name.Contains("tb_class"))
                tb.Add(con as TextBox);
        return tb.OrderByDescending(x => int.Parse(x.Name.Replace("tb_class", ""))).ToList();
    }

I didn't even think about adding int.Parse

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
  • Please show the data before replacing. Apparently you have sorted lexicographically on strings, not on a numerical type. – Codor Jun 27 '16 at 13:26
  • 3
    It's simple enough - you're processing the names of the controls, which are strings. If you're ordering alphabetically, they are in order! You need to compare them as a numeric type to get it to go in the order you want. – Bridge Jun 27 '16 at 13:26
  • You can use Natural Sort Order for this kind of thing - [have a look at my answer here](http://stackoverflow.com/a/31538443/106159) and [this answer here](http://stackoverflow.com/a/248613/106159). – Matthew Watson Jun 27 '16 at 13:40

7 Answers7

3

You have to convert it to an int, here's a LINQ only approach:

private List<TextBox> GetCustomClasses()
{
    return gb_customClasses.Controls.OfType<TextBox>()
        .Where(txt => txt.Name.StartsWith("tb_class"))
        .OrderByDescending(txt => int.Parse(txt.Name.Substring("tb_class".Length)))
        .ToList();
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
2

You must make a numerical sorting, rather than lexicographic. Try this:

return tb.OrderByDescending(x => int.Parse(x.Name.Replace("tb_class", ""))).ToList();

Note, you might not have to call .ToList(), depending on your case. Returning an IEnumerable<TextBox> instead might be useful - so do check that out.


Don't be scared to use the Tag property to store some extra information on your controls! This solution is somewhat nicer:

return tb.OrderByDescending(x => (int)x.Tag);

or

return tb.OrderByDescending(x => ((MyClass)x.Tag).Index);

You just have to make sure you add an appropriate Tag for any TextBox you add to gb_customClasses.Controls. I would tend towards this approach if the controls are dynamically created (then tagging is easy, and naming not even a must)

SimpleVar
  • 14,044
  • 4
  • 38
  • 60
0

You're sorting them alphabetically, change your return statement to:

return tb.OrderByDescending(x => Int32.Parse(x.Name.Replace("tb_class", ""))).ToList();

In order to get the list sorted numerically

Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
0

You should be comparing them as numbers, and not strings:

return tb.OrderByDescending(x => Convert.ToInt32(x.Name.Replace("tb_class", ""))).ToList();

Adding that little bit converts the name where you removed the tb_class from a string to an int.

Sunny Patel
  • 7,830
  • 2
  • 31
  • 46
0

modify to

OrderByDescending(x => Convert.ToInt32(x.Name.Replace("tb_class", "")))

Jack
  • 67
  • 2
0

I have these TextBoxes labeled as "tb_class#" and # = 1-10

With such encoding, you can utilize the fact that numbers with more digits are bigger, and also LINQ sort is stable. In other words, simply order first by the text length, then by the text:

return tb.OrderByDescending(x => x.Name.Length).ThenByDescending(x => x.Name).ToList();
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
-2

This might work:

private List<TextBox> GetCustomClasses()
{
    List<TextBox> tb = new List<TextBox>();

    List<String> indexes = new List<string>();

    //  N.B. A more conservative implementation might want to go for an unsigned 
    //  64-bit thing here. Just in case you run into a really big GroupBox.
    //  
    //  **WARNING** Due to Brexit, UK compilers will be changing to base 12, which 
    //  may adversely affect the performance of this loop. Also keep an eye out for 
    //  unpredictable fluctuations in the exchange rate with UK integers. 

    for (String i = "0"; Int32.Parse(i) < 0x7fffffff; i = (Int32.Parse(i) + 1).ToString())
    {
        indexes = indexes.Union(new string[] { i }).ToList();
    }

    var arrayOfIndexes = indexes.ToArray();

    //  Leave room for null terminator
    var reordered = new TextBox[gb_customClasses.Controls.Count + 1];

    foreach (Control con in gb_customClasses.Controls)
        if (con.Name.Contains("tb_class"))
            reordered[arrayOfIndexes.ToList().IndexOf(con.Name.Replace("tb_class", ""))] = con as TextBox;

    return reordered.ToList();
}