0

In my wpf application I have a listview containing elements where I want to be able to write position attributes following formats: 1,2,3; 1a, 1b, 1c; 1)a), 1)b), 1)c); 1.1, 1.2, 1.3;

These position attributes are of type string and I want to order them automatically by size and substeps.

I tried this:

public class ClassXY
{
    public string Position;
}

ObservableCollection<ClassXY> _myCollection = new ...;
_myCollection.OrderBy(p => p.Position);
_myCollection.OrderBy(p => Convert.ToDouble(p.Position));

Of course this didn't work for me, substeps in any format are always added at the end. Is there a way to to this without completely doing it on my own?

Phyti1
  • 31
  • 1
  • 6
  • 2
    Would [ThenBy](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.thenby?view=netframework-4.8) work for you? If not, you might have to write a [custom comparer](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby?view=netframework-4.8#System_Linq_Enumerable_OrderBy__2_System_Collections_Generic_IEnumerable___0__System_Func___0___1__System_Collections_Generic_IComparer___1__) – stuartd Aug 15 '19 at 23:02
  • I can think of two possibilities. 1) write a function that canonicalizes the inputs into a string that is sortable, and then sort by the output of that function. 2) I'm also pretty sure you can include a comparison function (that takes a pair of inputs and determines which is greater (or that they are equal). The second choice is probably cleaner, but make sure you meet all the contractual guarantees for that comparer. – Flydog57 Aug 15 '19 at 23:11
  • Can you edit your question to show an example of your actual input and desired output? – stuartd Aug 15 '19 at 23:18

2 Answers2

1

Besides needing ThenBy, you also need to remember that OrderBy isn't in place but returns an ordered IEnumerable

public class ClassXY
{
    public string Position;
}

ObservableCollection<ClassXY> _myCollection = new ...;
var orderedCollection = 
      myCollection.OrderBy(p => p.Position)
                  .ThenBy(p => Convert.ToDouble(p.Position));

Just as an FYI, OrderBy and ThenBy also come as OrderByDescending and ThenByDescending so you don't have to negate a condition and sacrifice readability for simple descending ordering.

MPost
  • 535
  • 2
  • 7
1

If anyone is interesed how I solved the problem: I used the solution found here How do I sort strings alphabetically while accounting for value when a string is numeric?

It worked fine for my purpose.

ObservableCollection<ClassXY> _myCollection = new ...;
_myCollection.OrderBy(p => p.Position, new SemiNumericComparer());

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (IsNumeric(s1) && IsNumeric(s2))
        {
            if (Convert.ToDouble(s1) > Convert.ToDouble(s2)) return 1;
            if (Convert.ToDouble(s1) < Convert.ToDouble(s2)) return -1;
            if (Convert.ToDouble(s1) == Convert.ToDouble(s2)) return 0;
        }

        if (IsNumeric(s1) && !IsNumeric(s2)) { return -1; }
        if (!IsNumeric(s1) && IsNumeric(s2)) { return 1; }
        return string.Compare(s1, s2, true);
    }

    public static bool IsNumeric(object value)
    {
        try
        {
            var i = Convert.ToDouble(value.ToString());
            return true;
        }
        catch (FormatException)
        {
            return false;
        }
    }
}
Phyti1
  • 31
  • 1
  • 6