【问题标题】:Sorting chapter stuff like 14.1.2.3 and 14.10.1.2.3.4对章节内容进行排序,例如 14.1.2.3 和 14.10.1.2.3.4
【发布时间】:2012-01-23 12:22:18
【问题描述】:

我有不同深度的不同章节。

所以有 14.1 和 14.4.2 和 14.7.8.8.2 等等。

按字母数字排序的 14.10 会出现在 14.2 之前。那很糟。它应该在 14.9 之后。

有没有一种简单的方法来对这些进行排序,而无需添加前导零? f.e.使用 linq?

【问题讨论】:

  • 我认为接受的答案here 也适用于您的情况。尽管您将被限制为四个“级别”。
  • 看起来相当不错,有 4 个级别 - 好的。更多的想法会很好:)

标签: c# linq sorting


【解决方案1】:
public class NumberedSectionComparer : IComparer<string>
{
  private int Compare(string[] x, string[]y)
  {
    if(x.Length > y.Length)
      return -Compare(y, x);//saves needing separate logic.
    for(int i = 0; i != x.Length; ++i)
    {
      int cmp = int.Parse(x[i]).CompareTo(int.Parse(y[i]));
      if(cmp != 0)
        return cmp;
    }
    return x.Length == y.Length ? 0 : -1;
  }
  public int Compare(string x, string y)
  {
    if(ReferenceEquals(x, y))//short-cut
      return 0;
    if(x == null)
      return -1;
    if(y == null)
      return 1;
    try
    {
      return Compare(x.Split('.'), y.Split('.'));
    }
    catch(FormatException)
    {
      throw new ArgumentException();
    }
  }
}

【讨论】:

    【解决方案2】:

    我现在就这样做了,需要一些测试:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace TestesConsole
    {
        class Program
        {
            static void Main(string[] args)
            {
                string[] vers = new[]
                                  {
                                      "14.10",
                                      "14.9",
                                      "14.10.1",
                                  };
    
    
                var ordered = vers.OrderBy(x => x, new VersionComparer()).ToList();
    
            }
        }
    
        public class VersionComparer : IComparer<string>
        {
            public int Compare(string x, string y)
            {
                string[] xs = x.Split('.');
                string[] ys = y.Split('.');
    
                int maxLoop = Math.Min(xs.Length, ys.Length);
    
                for (int i = 0; i < maxLoop; i++)
                {
                    if(int.Parse(xs[i]) > int.Parse(ys[i]))
                    {
                        return 1;
                    }
                    else if(int.Parse(xs[i]) < int.Parse(ys[i]))
                    {
                        return -1;
                    }
                }
    
                if(xs.Length > ys.Length)
                {
                    return 1;
                }
                else if(xs.Length < ys.Length)
                {
                    return -1;
                }
    
                return 0;
            }
        }
    }
    

    【讨论】:

    • 我首先测试了这个解决方案,由于某种原因,它在大规模处理中运行,并以最大执行时间结束......非常感谢您的时间,但对我来说,出于某种神秘的原因它没有效果很好。 :|
    • 我没有加载测试,只是为了好奇,你的收藏中有多少个项目?
    • 大约有 1500 个不同的项目,总共 1700 个或其他东西 :) @fujiy
    【解决方案3】:
    var headers = new List<string> {"14.1.2.3", "14.1", "14.9", "14.2.1", "14.4.2", "14.10.1.2.3.4", "14.7.8.8.2"};
        headers.Sort(new MySorter());
    
    
    
    class MySorter : IComparer<string>
        {
        public int Compare(string x, string y)
        {
        IList<string> a = x.Split('.');
        IList<string> b = y.Split('.');
        int numToCompare = (a.Count < b.Count) ? a.Count : b.Count;
        for (int i = 0; i < numToCompare; i++)
        {
        if (a[i].Equals(b[i]))
        continue;
        int numa = Convert.ToInt32(a[i]);
        int numb = Convert.ToInt32(b[i]);
         return numa.CompareTo(numb);
        }
        return a.Count.CompareTo(b.Count);
        }
    
        }
    

    【讨论】:

      【解决方案4】:

      使用 IComparer 有一个很大的缺点是经常重复相当昂贵的计算,所以我认为预先计算一个订单标准是一个好主意:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      
      namespace ChapterSort
      {
          class Program
          {
              static void Main(string[] args)
              {
                  String[] chapters=new String[] {"14.1","14.4.2","14.7.8.8.2","14.10","14.2","14.9","14.10.1.2.3.4","14.1.2.3" };  
      
                  IEnumerable<String> newchapters=chapters.OrderBy(x => new ChapterNumerizer(x,256,8).NumericValue);
      
                  foreach (String s in newchapters) Console.WriteLine(s);
      
              }
          }
      
          public class ChapterNumerizer
          {
              private long numval;
      
              public long NumericValue {get{return numval;}}
      
              public ChapterNumerizer (string chapter,int n, int m)
              {
                  string[] c = chapter.Split('.');
                  numval=0;
                  int j=0;
      
                 foreach (String cc in c)
                 {
                     numval=n*numval+int.Parse(cc);
                     j++;
                 }
                 while (j<m)
                 {
                     numval*=n;
                     j++;
                 }
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        这个解决方案更通用。

        public class SequenceComparer<T> : IComparer<IEnumerable<T>> where T : IComparable<T>
        {
            public int Compare(IEnumerable<T> x, IEnumerable<T> y)
            {
                IEnumerator<T> enx = x.GetEnumerator();
                IEnumerator<T> eny = y.GetEnumerator();
        
                do
                {
                    bool endx = enx.MoveNext();
                    bool endy = eny.MoveNext();
        
                    if (!endx && !endy)
                        return 0;
        
                    if (!endx)
                        return -1;
        
                    if (!endy)
                        return 1;
        
                    var comp = enx.Current.CompareTo(eny.Current);
                    if(comp != 0)
                        return comp;
                } while (true);
            }
        }
        

        然后使用:

        var sv = vers.Select(v => new { Key = v, Split = v.Split('.').Select(Int32.Parse) });
        var ordered = sv.OrderBy(x => x.Split, new SequenceComparer<int>()).Select(x => x.Key);
        

        【讨论】:

          【解决方案6】:

          作为一个小的 LINQ 单行:

          List<string> chapters= new List<string>()
          {
              "14.1",
              "14.4.2",
              "14.7.8.8.2",
              "14.10",
              "14.2"
          };
          
          chapters.OrderBy(c => Regex.Replace(c, "[0-9]+", match => match.Value.PadLeft(10, '0')));
          

          独立于关卡但肯定不是最好的表现...

          积分将转到https://stackoverflow.com/a/5093939/226278

          【讨论】:

            猜你喜欢
            • 2020-08-14
            • 1970-01-01
            • 2016-05-17
            • 2011-01-24
            • 1970-01-01
            • 2012-05-16
            • 2013-06-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多