【问题标题】:Sort String List Numerically对字符串列表进行数字排序
【发布时间】:2016-10-26 05:05:05
【问题描述】:

我有以下字符串列表:

List<string> a = new List<string>();

a.Add("2015");
a.Add("2015 /M1");
a.Add("2015 /M2");
a.Add("2015 /M9");
a.Add("2015 /M10");
a.Add("2015 /M11");
a.Add("2015 /M12");
a.Add("2015 /M3");
a.Add("2015 /M4");
a.Add("2015 /M5");
a.Add("2015 /M6");
a.Add("2015 /M7");
a.Add("2015 /M8");

当我调用排序函数a.sort() 时,它的排序如下:

2015
2015 /M1
2015 /M11
2015 /M12
2015 /M2
2015 /M3
2015 /M4
2015 /M5
2015 /M6
2015 /M7
2015 /M8
2015 /M9

但是我该如何修改使它看起来像下面。

2015
2015 /M1
2015 /M2
2015 /M3
2015 /M4
2015 /M5
2015 /M6
2015 /M7
2015 /M8
2015 /M9
2015 /M10
2015 /M11
2015 /M12

我在其他列表项中也有相同的模式,例如 2015 Q/12、2015 Q/11 等。

【问题讨论】:

  • 实现 IComparer 接口并执行您的自定义排序算法并将其传递给 Sort 方法。 MSDN 是你的朋友。
  • 您的输入格式是否总是“yyyy X/MM”,yyyy 是年份,X 是任意随机字符,MM 是月份?对您的问题的简短回答是按 int 排序,而不是按字符串排序。
  • 我需要在字符串列表中完成它,因为我有 2015 Q/1、2015 Q/2 以及这些值是动态创建的。

标签: c# linq list sorting


【解决方案1】:

您需要从字符串中提取数字,可以通过正则表达式完成。然后将其转换为整数并按其排序。

var e = from s in a
        let g = Regex.Match(s, @"^\d+(?: \/[MQ](\d+))?$")
        let n = g.Groups[1].Value != "" ? int.Parse(g.Groups[1].Value) : (int?)null
        orderby n
        select s;

a = e.ToList();

编辑

先按年份排序,然后使用以下代码

var e = from s in a
        let g = Regex.Match(s, @"^[A-Za-z]*(\d+)(?: \/[MQ](\d+))?$")
        let y = g.Groups[1].Value != "" ? int.Parse(g.Groups[1].Value) : 0
        let m = g.Groups[2].Value != "" ? int.Parse(g.Groups[2].Value) : 0                    
        orderby y, m      
        select s;

【讨论】:

  • 如果要排序 2015 Q/1 与 2015 M/1 相同的字符串怎么办?
  • @Asim 您希望 /Q1 在 /M1 之后还是 /M3 之后排序?
  • @NiyokoYuliawan 我已经测试了你的方法,它解决了几个月的问题,我可以修改它以使其也适用于 Q/1。
  • @NiyokoYuliawan 谢谢,我如何修改它以在开始时添加 CY 或 FY,如 CY2015 /M1。在整个字符串中它将是 CY 或 FY。谢谢
  • @Asim 将您的正则表达式模式更改为^[A-Za-z]*\d+(?: \/[MQ](\d+))?$
【解决方案2】:

我这样做了:

var results =
    a
        .OrderBy(x => new string(x.Take(7).ToArray()))
        .ThenBy(x => int.Parse(new string(x.Skip(7).DefaultIfEmpty('0').ToArray())));

...得到了这个:

2015 2015 /M1 2015 /平方米 2015 /M3 2015 /M4 2015 /M5 2015 /M6 2015 /M7 2015 /M8 2015 /M9 2015 /M10 2015 /M11 2015 /M12

【讨论】:

    【解决方案3】:

    您正在寻找natural sort order。 在链接的问题中,您可以找到一个纯 LINQ 实现,该实现可用于任何自然排序问题。

    【讨论】:

      【解决方案4】:

      This 的帖子与您的相似。根据您的要求进行编辑:

      var sortedList = a.OrderBy(x => PadNumbers(!x.Contains("M")? "" : x.Substring(x.IndexOf('M'), (x.Length - x.IndexOf('M'))))).ToList();
      
      public static string PadNumbers(string input)
      {
          return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
      }
      

      【讨论】:

        【解决方案5】:

        我已经详细阐述了 Niyoko Yuliawan 的解决方案,结果是:

          class StringComparer : IComparer<string>
          {
            const string pattern = @"^\D*(?<year>\d{4})( \/[MQ](?<index>\d+))?$";
        
            public int Compare(string x, string y)
            {
              var mx = Regex.Match(x, pattern);
              var my = Regex.Match(y, pattern);
        
              int ix;
              if (int.TryParse(mx.Groups["index"].Value, out ix))
              {
                int iy;
                if (int.TryParse(my.Groups["index"].Value, out iy))
                {
                  return ix.CompareTo(iy);
                }
              }
        
              return mx.Groups["year"].Value.CompareTo(my.Groups["year"].Value);
            }
          }
        
          class Program
          {
            static void Main(string[] args)
            {
        
              List<string> a = new List<string>();
        
              a.Add("2015");
              a.Add("2015 /Q1");
              a.Add("CY2015 /Q2");
              a.Add("2015 /Q9");
              a.Add("2015 /Q10");
              a.Add("2015 /Q11");
              a.Add("2015 /Q12");
              a.Add("2015 /Q3");
              a.Add("2014");
              a.Add("2015 /Q4");
              a.Add("2015 /Q5");
              a.Add("2015 /Q6");
              a.Add("2015 /Q7");
              a.Add("2015 /Q8");
        
              a.Sort(new StringComparer());
        
              foreach (var x in a)
              {
                Console.WriteLine(x);
              }
        
              Console.WriteLine("END");
              Console.ReadLine();
        
            }
          }
        

        它避免了临时序列(e)。

        【讨论】:

          【解决方案6】:

          你也可以轻松地为这个排序函数编写你自己的扩展:

              public static class Extension
          {
              public static List<string> sortItMyWay(this List<string> mylist)
              {
                  string temp = string.Empty;
          
                  for (int write = 0; write < mylist.Count; write++)
                  {
                      for (int sort = 0; sort < mylist.Count - 1; sort++)
                      {
                          if (mylist[sort].Weight() > mylist[sort + 1].Weight())
                          {
                              temp = mylist[sort + 1];
                              mylist[sort + 1] = mylist[sort];
                              mylist[sort] = temp;
                          }
                      }
                  }
                  return mylist;
              }
          
          
          public static int Weight (this string input)
          {
              var value = 0;
              for (int i = input.Length - 1; i >= 0 ; i--)
              {
                  value += input[i] * (int)Math.Pow(10,i);
              }
              return value;
          }
          

          }

          【讨论】:

          • 这如何回答这个问题?
          • 这是按字符串长度有效排序吗?如果字符串太长(由于 `Math.Pow),它会失败。这根本不是一个明显的解决方案。你能解释一下吗?
          • 这个权重函数给每个字符串一个 int 值。它不是按长度排序的!你对长度的限制是正确的,但这个问题没关系。这个扩展可能不是最佳实践,但提问者可以按照他喜欢的方式更改权重函数,对于这个问题以及他喜欢的其他排序方式。在我看来,这比仅仅复制正则表达式具有更高的学习效果,而无需了解它是如何工作的。 sortItMyWay 函数只是一个冒泡排序,使用权重函数。
          猜你喜欢
          • 2015-03-26
          • 2021-06-10
          • 2017-09-26
          • 2021-02-21
          • 2012-04-03
          • 2011-03-26
          • 2018-07-02
          相关资源
          最近更新 更多