c#以不同格式排序字符串

时间:2017-11-28 12:55:47

标签: c# .net linq

我有车牌号码,我返回到UI,我希望它们以asc顺序排序:

所以让我们说输入如下:

1/12/13/2
1/12/11/3
1/12/12/2
1/12/12/1

我的预期输出是:

1/12/11/3
1/12/12/1
1/12/12/2
1/12/13/2

我目前的代码是:

var orderedData = allLicenceNumbers
   .OrderBy(x => x.LicenceNumber.Length)
   .ThenBy(x => x.LicenceNumber)
   .ToList();

但是对于另一个输入样本如下:

4/032/004/2
4/032/004/9
4/032/004/3/A
4/032/004/3/B
4/032/004/11

我将数据返回为:

4/032/004/2
4/032/004/9
4/032/004/11
4/032/004/3/A
4/032/004/3/B

当我需要的是:

4/032/004/2
4/032/004/3/A
4/032/004/3/B
4/032/004/9
4/032/004/11

我是否有更好的方法可以简单地在两个样本输入中给出正确的结果,或者我需要编写自定义排序?

修改

它不会总是字符串上的相同元素。

这可以是示例输入:

2/3/5/1/A
1/4/6/7
1/3/8/9/B
1/3/8/9/A
1/5/6/7

预期输出为:

1/3/8/9/A
1/3/8/9/B
1/4/6/7
1/5/6/7
2/3/5/1/A

3 个答案:

答案 0 :(得分:3)

您应该拆分数字并将每个部分相互比较。按字典顺序比较数字和字符串。

var licenceNumbers = new[]
{
    "4/032/004/2",
    "4/032/004/9",
    "4/032/004/3",
    "4/032/004/3/A",
    "4/032/004/3/B",
    "4/032/004/11"
};

var ordered = licenceNumbers
    .Select(n => n.Split(new[] { '/' }))
    .OrderBy(t => t, new LicenceNumberComparer())
    .Select(t => String.Join("/", t));

使用以下比较器:

public class LicenceNumberComparer: IComparer<string[]>
{ 
    public int Compare(string[] a, string[] b)
    {
        var len = Math.Min(a.Length, b.Length);
        for(var i = 0; i < len; i++)
        {
            var aIsNum = int.TryParse(a[i], out int aNum);
            var bIsNum = int.TryParse(b[i], out int bNum);
            if (aIsNum && bIsNum)
            {
                if (aNum != bNum)
                {
                    return aNum - bNum;
                }
            }
            else
            {
                var strCompare = String.Compare(a[i], b[i]);
                if (strCompare != 0)
                {
                    return strCompare;
                }
            }
        }
        return a.Length - b.Length;
    }
}

答案 1 :(得分:1)

如果我们可以假设

  1. 由'/'分隔的若干(一个或多个)部分的车牌号码,例如, 40320042
  2. 每个部分不长于某个常量值(下面的代码中为3
  3. 每个部分都包含数字(例如4032)或非数字(例如AB
  4. 我们只能PadLeft每个号码牌的数字部分0,以便不比较"3""11"(并获得"3" > "11"),但填充{ {1}}:

    "003" < "011"

    结果:

      var source = new string[] {
        "4/032/004/2",
        "4/032/004/9",
        "4/032/004/3/A",
        "4/032/004/3/B",
        "4/032/004/11",
      };
    
      var ordered = source
        .OrderBy(item => string.Concat(item
           .Split('/')                            // for each part
           .Select(part => part.All(char.IsDigit) // we either
               ? part.PadLeft(3, '0') // Pad digit parts e.g. 3 -> 003, 11 -> 011 
               : part)));             // ..or leave it as is
    
      Console.WriteLine(string.Join(Environment.NewLine, ordered));
    

答案 2 :(得分:0)

您似乎想要在数字而不是字符串模式中对字符串的第四个元素(由/分隔)进行排序。?

你可以通过像{}

中的任何其他方法代码块那样使lambda更多参与/多语句
var orderedData = allLicenceNumbers
 .OrderBy(x => 
  { 
   var t = x.Split('/');
   if(t.Length<4)
     return -1;
   else{
     int o = -1;
     int.TryParse(t[3], out o);
     return o;
  }
 )
 .ToList();

如果您在对字符串的更多元素进行排序之后,您可能想要查看一些替代逻辑,也许如果字符串的第一部分将始终采用N / NNN / NNN / ?? / ?,然后执行:

var orderedData = allLicenceNumbers
 .OrderBy(w => w.Remove(9)) //the first 9 are always in the form N/NNN/NNN
 .ThenBy(x =>               //then there's maybe a number that should be parsed
  {  
   var t = x.Split('/');
   if(t.Length<4)
     return -1;
   else{
     int o = -1;
     int.TryParse(t[3], out o);
     return o;
  }
 )
 .ThenBy(y => y.Substring(y.LastIndexOf('/'))) //then there's maybe A or B..
 .ToList();

最终,似乎越来越多的异常值将被投入到混合中,所以你只需要继续发明规则来排序......

或者更改你的字符串以标准化所有内容(例如,以NNN / NNN / NNN / NNN / NNA格式为单位),然后排序为字符串..

var orderedData = allLicenceNumbers
 .OrderBy(x =>               
  {  
   var t = x.Split('/');
   for(int i = 0; i < t.Length; i++) //make all elements in the form NNN
   {
     t[i] = "000" + t[i];
     t[i] = t[i].Substring(t[i].Length - 3);
   }
   return string.Join(t, "/");
  }
 )
 .ToList();
嗯......讨厌!