2

I have these items:

FOD19-1,FOD33-2,FOD39,SÅL1,SÅL23-3,SÅL31-1,SÅL32-2,SÅL33-1,SÅL7-1

I use this sort method to sort them:

List<OrderDetailItem> orderDetails = new 
BLOrderDetail().GetOrderDetails(orderId).OrderBy(t => t.Sku, 
StringComparer.Ordinal).ToList();

So the expected and desired result would be:

FOD19-1,FOD33-2,FOD39,SÅL1,SÅL7-1,SÅL23-3,SÅL31-1,SÅL32-2,SÅL33-1

As you can see I want SÅL7-1 to be placed right after SÅL1 instead.

How do I accomplish this?

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Nick Developer
  • 277
  • 1
  • 4
  • 15
  • 1
    So you want to order items to be ascending? – yoger Jan 22 '19 at 15:18
  • What is the expected result? – Elton Santana Jan 22 '19 at 15:20
  • 1
    You're comparing the actual string values of these (assuming `Sku` is a `String` type), so when you get to SÅL7-1 and SÅL23-3, the issue is that a "7" is greater than the "2" of the "23". It isn't going to compare the "7" to the entire "23". You'll want to implement some sort of custom comparer on the class/`Sku` so that it can compare each part separately, and treat integer sections as entire integers. – Broots Waymb Jan 22 '19 at 15:34
  • 1
    It sounds like you want to use [Natural Sort Order in C#](https://stackoverflow.com/a/248613/1115360). – Andrew Morton Jan 22 '19 at 15:34
  • You have to extract the numbers from the input strings (for example 19 and 1 from FOD19-1; 7 and 1 from SÅL7-1) so you can compare them as integers instead of strings. The reason for this is that string comparison happens per character. – Lennart Jan 22 '19 at 15:46
  • As @BrootsWaymb say. Consider implementing a custom compare. You can implement the IComparable interface in OrderDetailItem class. With this, you can user Sort method. –  Jan 22 '19 at 15:58

3 Answers3

1

Well provided that all id entries start with three chars and are followed by number in xx-xx format you could split it to to key and value pairs where key is string part and value is your number converted to double.

Then we sort this selection by first key (string) and then value (double) and return the actual input.

That is what it might look like:

var str = new[]
{
   "FOD19-1","FOD33-2","FOD39","SÅL1","SÅL23-3","SÅL31-1","SÅL32-2","SÅL33-1","SÅL7-1"
};
var decimalSeparator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;

var orderDetailsDict = new BLOrderDetail().GetOrderDetails(orderId).ToDictionary(o => o.Sku, o => o) ;

var orderDetailsSorting = orderDetailsDict.Keys.Select(s =>
          {
              var key = s.Substring(0, 3);
              var value = double.Parse(s.Replace(key, "").Replace("-", decimalSeparator));
              return Tuple.Create(key, value, s);
          })
          .OrderBy(x => x.Item1)
          .ThenBy(x => x.Item2)
          .Select(x => x.Item3);

List<OrderDetailItem> orderDetails = orderDetailsSorting.Select(key => rorderDetailsDict[key]).ToList();

In case when key part could be of different length you could find a first digit in string and do: s.Substring(0, firstDigitIndex) to get the key

Fabjan
  • 13,506
  • 4
  • 25
  • 52
1

Here is a custom compare using Regex

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication98
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] input = { "FOD19-1", "FOD33-2", "FOD39", "SÅL1", "SÅL23-3", "SÅL31-1", "SÅL32-2", "SÅL33-1", "SÅL7-1" };
            Array.Sort(input, new MyCompare());
            // Or
            //var results = input.OrderBy(x => x, new MyCompare()).ToList();
        }
    }
    public class MyCompare : IComparer<string>
    {
        const string pattern = @"(?'name'[^\d]+)(?'index'\d+)?-?(?'subindex'\d+)?";
        public int Compare(string a, string b)
        {
            if (a == b) return 0;
            int results = 0;
            Match matcha = Regex.Match(a, pattern);
            Match matchb = Regex.Match(b, pattern);

            string namea = matcha.Groups["name"].Value;
            int indexa = 0;
            int subindexa = 0;
            Boolean isAindexInt = int.TryParse(matcha.Groups["index"].Value, out indexa);
            Boolean isAsubindexInt = int.TryParse(matcha.Groups["subindex"].Value, out subindexa);

            string nameb = matchb.Groups["name"].Value;
            int indexb = 0;
            int subindexb = 0;
            Boolean isBindexInt = int.TryParse(matchb.Groups["index"].Value, out indexb);
            Boolean isBsubindexInt = int.TryParse(matchb.Groups["subindex"].Value, out subindexb);

            results = namea.CompareTo(nameb);

            if (results == 0)
            {
                results = isAindexInt.CompareTo(isBindexInt);
                if (results == 0)
                {
                    results = indexa.CompareTo(indexb);
                    if (results == 0)
                    {
                        results = isAsubindexInt.CompareTo(isBsubindexInt);
                        if (results == 0)
                        {
                            results = subindexa.CompareTo(subindexb);
                        }
                    }
                }
            }
            return results;
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

Could you post your OrderDetailItem Class? Are the values in the class you want to compare strings?

A possible Solution is to implement the IComparable Interface in your OrderDetailItem. So you can define how the single OrderDetailItems can be compared and sorting will be no problem.

Gino Pensuni
  • 356
  • 4
  • 15
  • I am only taking the SKU from orderDetailItem which is a string and consists of the above: FOD19-1,FOD33-2,FOD39,SÅL1,SÅL23-3,SÅL31-1,SÅL32-2,SÅL33-1,SÅL7-1 – Nick Developer Jan 22 '19 at 15:40
  • What about the idea to make SKU an own type and SKU implements the IComparable interface? If this is not possible (because you can not access the OrderDetailItem class code), you could write an own Comparer implementing the IComparer interface. – Gino Pensuni Jan 22 '19 at 15:59
  • When writing your own comparison implementation you could compare character by character: if you have two strings you could take of each string the first character and see which is "greater" (because each character has an int value behind). If they are equal, you could take the next two characters and look which is bigger. If one character is smaller then the other, you know that the string containing the smaller character must be on the left side and so on... – Gino Pensuni Jan 22 '19 at 16:16