Сортировка материалов главы, таких как 14.1.2.3 и 14.10.1.2.3.4 - PullRequest
8 голосов
/ 23 января 2012

У меня есть различные главы с различной глубиной.

, поэтому есть 14.1 и 14.4.2 и 14.7.8.8.2 и т. Д.14.2.Это плохо.Это должно наступить после 14,9.

Есть ли простой способ сортировки без добавления начальных нулей?например, с linq?

Ответы [ 6 ]

7 голосов
/ 23 января 2012
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();
    }
  }
}
4 голосов
/ 23 января 2012

Я сделал это прямо сейчас, нужно несколько тестов:

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;
        }
    }
}
1 голос
/ 19 апреля 2017

Как небольшой однострочный 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

1 голос
/ 23 января 2012

Это решение более общее.

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);
1 голос
/ 23 января 2012

Использование 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++;
           }
        }
    }
}
1 голос
/ 23 января 2012
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);
    }

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...