对14.1.2.3和14.10.1.2.3.4等章节内容进行分类
本文关键字:分类 | 更新日期: 2023-09-27 18:21:04
我有不同深度的不同章节。
因此存在14.1和14.4.2以及14.7.8.8.2,依此类推
按字母数字排序的14.10将出现在14.2之前。太糟糕了。它应该在14.9之后。
有没有一种简单的方法可以在不添加前导零的情况下对se进行排序?和林克在一起?
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();
}
}
}
我现在就做了,需要一些测试:
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;
}
}
}
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);
}
}
使用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++;
}
}
}
}
此解决方案更通用。
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);
作为一个小型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