2

I have a project where I have a web store with items fetched from a list. Some items are named TERRAIN 1, TERRAIN 2 and so on. While other items are named for example BOAT.

The problem is that when I order the list alphabetically, TERRAIN 12 will show up before TERRAIN 2.

Is there any way I can order the list the way that I want: TERRAIN 1 TERRAIN 2 TERRAIN 12

and still be able to order the products that don't have numbers in an alphabetical order: BOAT CABIN TRAILER

I have tried splitting the names and ordering them in this way:

`var ordered = DisplayProducts.Select(s => new { Str = s, Split = s.Product.ProductName.Split('     ') })
        .OrderBy(x => x.Split[0])
        .ThenBy(x => int.Parse(x.Split[1]))
        .Select(x => x.Str)
        .ToList();`

It works well with the products that have numbers. However that causes problems when there's no value in x.Split[1], because the index is out of range.

hagru
  • 41
  • 5
  • You can store them in an object with properties, for example `public record Item(string Name, int? Number);` then use `OrderBy` and `ThenBy` methods – AoooR Mar 06 '23 at 14:58
  • you're using default comparer. You need a custom comparer to sort things the way you want – T.S. Mar 06 '23 at 14:59
  • 1
    https://stackoverflow.com/questions/49374610/natural-human-alpha-numeric-sort-in-microsoft-sql-server, https://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp, keyword: "natural sort". – CodeCaster Mar 06 '23 at 14:59
  • 4
    You seem to be asking for ["Natural sort"](https://en.wikipedia.org/wiki/Natural_sort_order). Perhaps knowing the conventional name will help in your research. – Damien_The_Unbeliever Mar 06 '23 at 14:59
  • There are some questions about this already, for example [How to Sort Integer Strings?](https://stackoverflow.com/questions/785309/how-to-sort-integer-strings/786318#786318). My preferred solution is to call the `StrCmpLogicalW` native win32 function. – JonasH Mar 06 '23 at 15:06

2 Answers2

1

replace

.ThenBy(x => int.Parse(x.Split[1]))

with

.ThenBy(x => int.TryParse(x.Split.ElementAtOrDefault(1), out int temp) ? temp : -1)
fubo
  • 44,811
  • 17
  • 103
  • 137
0

One approach you could take is to split the product names into separate components, and then sort each component separately.

For example, you could split the product name into two components: the first component being the text before the number, and the second component being the number itself. You could then sort the list first by the first component, and then by the second component as a number.

Here is an example implementation in C#:

var ordered = DisplayProducts.OrderBy(p => {
    var match = Regex.Match(p.Product.ProductName, @"^(\D+)(\d*)$");
    var alpha = match.Groups[1].Value;
    var num = match.Groups[2].Value;
    return (alpha, num);
}).ToList();

all of this is simply caused by lexographic ordering. you will encounter it both in your solution and in notepad++ auto sorting :)