首页 > 解决方案 > 按数字顺序排列字母数字字符串,然后按前缀/后缀

问题描述

我有一个复杂的排序模式要复制,我的解决方案似乎有点笨拙。我的输入是一个数字列表,可以有几个字母作为后缀,前缀都是字母('aaa'、'aab'、'ac' 等)。我需要按数字排序,然后按后缀(如果有的话)排序,然后按前缀(如果有的话)排序。

例如

"a1a",
"5ac",
"1",
"12",
"2",
"11",
"5aa",
"3",
"5ab",
"a2b",
"abb11ca",
"1b",
"aba11ca"

将被排序为

1
a1a
1b
2
a2b
3
5aa
5ab
5ac
11
aba11ca
abb11ca
12

这是我使用 Linq 提出的解决方案。

static void Main(string[] args)
{
    var arr = new []
    {"b2","a1a","5ac","1","12","2","11","5aa","3","5ab","a1","a2b","abb11ca","1b","aba11ca"
    };
    var ordered = arr.Select(str => {
                var parts = SplitIntoPrefixNumberSuffix(str);
                var number = int.Parse(parts[1]);
                return new { str, parts, number };
            })
            .OrderBy(x => x.number).ThenBy(x => x.parts[2]).ThenBy(x => x.parts[0])
            .Select(x => x.str);

    Console.WriteLine("sorted array: ");
    foreach (var s in ordered)
    {
        Console.WriteLine("{0}", s);
    }

    Console.ReadLine();
}      

public static string[] SplitIntoPrefixNumberSuffix(string str)
{
    var numChar = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
    var numLoc = str.IndexOfAny(numChar);
    var nums = "";
    foreach (var c in str)
    {
        if (char.IsDigit(c))
            nums = nums + c;
    }
    Console.WriteLine("numLoc: {0}; nums: {1}", numLoc, nums.Count());
    var prefix = str.Substring(0, numLoc);
    var suffix = str.Substring(numLoc + nums.Count());
    Console.WriteLine("prefix {0}; nums {1}; suffix {2}", prefix, nums, suffix);
    return new[] { prefix, nums, suffix };
}

这是它的一个 .netfiddle:https ://dotnetfiddle.net/C7ZA0b 。

虽然它有效,但感觉这不是一个很好的解决方案。我对集合进行了多次迭代,我认为我应该使用自定义的可比性。

我以前从未写过 Comparable;我查看了Dot Net Pearls Alphanumeric Sorting并且可以遵循它,但还不足以对其进行修改以满足我的需要。

是否有 IComparable 我可以用来完成上述工作?关于一个学习如何写的好地方有什么建议吗?

标签: c#.netlinqsorting

解决方案


因此,您可以使用正则表达式命名组来拆分字符串的各个组件,然后按每个组件排序:

var regex = new Regex(@"^(?<pre>\D*)(?<num>\d+)(?<suff>\D*)$");
var ordered = data.Select(d => (match: regex.Match(d), value: d))
    .Where(x => x.match.Success) //throw away anything that doesn't conform
    .Select(x => (
        x.value, 
        pre: x.match.Groups["pre"].Value, 
        num: int.Parse(x.match.Groups["num"].Value), 
        suff: x.match.Groups["suff"].Value))
    .OrderBy(x => x.num)
    .ThenBy(x => x.suff)
    .ThenBy(x => x.pre)
    .Select(x => x.value);

...但最终这与您的解决方案并没有什么不同。我真的看不出专业人士IComparer将如何简化这一点。

如果您没有可用的元组(< C#7.0),请交换匿名类:

data.Select(d => new { match = regex.Match(d), value = d})
    .Where(x => x.match.Success)
    .Select(x => new { 
        x.value, 
        pre = x.match.Groups["pre"].Value, 
        num = int.Parse(x.match.Groups["num"].Value), 
        suff = x.match.Groups["suff"].Value})
    .OrderBy(x => x.num)
    .ThenBy(x => x.suff)
    .ThenBy(x => x.pre)
    .Select(x => x.value)

推荐阅读