c# - 对字符串列表(门牌号)进行排序

标签 c# sorting

我有一个字符串列表(包含门牌号)如下:

List<string> myList = new List<string> { "11", "11a", "11 a", "11-1", "11a-1" };
myList.Sort(new MyComparer());

现在我希望列表被排序为 { "11", "11a", "11 a", "11a-1", "11-1" }首先是所有没有后缀的软管编号,然后是后面有字母的软管编号,然后是所有其他软管编号。所以我将我的字符串分成一个前缀(实际数字)和一个后缀(数字本身后面的所有内容)。

private class MyComparer : IComparer<string>
{

    protected virtual int compareHouseNumbers(string hnr1, string hnr2)
    {
        // ...
        // split number and suffix
        // ...

        // housenumbers (integers) are also equal so let the suffix decide which one is greater
        // the suffixes do not contain any spaces now
        if (String.IsNullOrEmpty(suffix1)) return -1;
        else if (String.IsNullOrEmpty(suffix2)) return 1;

        // the following shell ensure that a letter comes "before" any other special char (such as "-" or "/")
        if (Char.IsLetter(suffix1.FirstOrDefault()) && !Char.IsLetter(suffix2.FirstOrDefault())) return -1;
        else if (!Char.IsLetter(suffix1.FirstOrDefault()) && Char.IsLetter(suffix2.FirstOrDefault())) return 1;
        // if we have more complexity (compare 11a-1 with 11a-2) we use standard string-comparison
        return String.Compare(suffix1, suffix2);
    }

    /// <inheritDoc/>
    public int Compare(string lbz1, string lbz2)
    {
        return this.compareHouseNumbers(lbz1, lbz2);
    }
}

但是我在根据这些后缀对列表进行排序时遇到了问题。我得到的列表是 {"11", "11 a", "11a", "11-1", "11a-1"}虽然互换条目 "11a""11 a"对我们的目的来说没问题我不明白为什么最后一个条目是"11a-1"而不是 "11-1" .我已经通过比较进行了调试,但显然这两个成员从未直接比较过,因此很难理解这里到底发生了什么。我该怎么做才能获得以 "a" 开头的后缀在那些没有之前?

如果可能有更优雅的方法来实现这一点,我愿意接受任何改进意见。

编辑:将输入拆分为实际数字和后缀主要是使用此正则表达式 (\\d+)\\s*(\\S*) 完成的.这导致整数部分(实际门牌号)和该数字之后的字符串部分。之后,我们仅使用 suffix1 = suffix1.Trim(' ', '-', '/'); 从后缀中删除任何非字母数字字符。 (suffix2 适当)话虽如此,我们实际上并没有比较 -1a-1但是1a-1 .然而,这不会改变结果本身的任何内容(因为 -11 在 lecographically 小于 a )。

EDIT2:我删除了列表中的一些成员,以便只剩下两个有问题的成员:List<string> myList = new List<string> { "11-1", "11a-1" };更改后,排序结果符合预期:{ "11a-1", "11-1" }

EDIT3:我刚刚更改了成员在列表中的顺序(将 11 放在列表末尾)。现在结果也符合预期。所以它似乎取决于列表中元素的初始顺序。很奇怪...

最佳答案

尝试以下操作,您没有给出如何拆分前缀和后缀的示例,所以我自己编了一个:

private class MyComparer : IComparer<string>
{

    private static readonly Regex matchRegex = new Regex(@"^(?<prefix>\d+)(?<spaces>\s*)(?<suffix>.*?)$");


    private int match(string compare, out string prefix, out string suffix) {
        var match= matchRegex.Match(compare);
        prefix=match.Groups["prefix"].Value;
        suffix=match.Groups["suffix"].Value;
        return match.Groups["spaces"].Value.Length;
    }
    protected virtual int compareHouseNumbers(string hnr1, string hnr2)
    {
        // ...
        // split number and suffix
        // ...
        string prefix1;
        string prefix2;
        string suffix1;
        string suffix2;
        var spaces1 = match(hnr1, out prefix1,out suffix1);
        var spaces2 = match(hnr2, out prefix2,out suffix2);

        Debug.WriteLine("Comparing '{0}' and '{1}'",suffix1,suffix2);
        var prefixCompare = String.Compare(prefix1,prefix2);
        if (prefixCompare != 0) {
            return prefixCompare;
        }
        // housenumbers (integers) are also equal so let the suffix decide which one is greater
        // the suffixes do not contain any spaces now

        // FIX IS HERE!!! 
        // Previous code would compare "11" and "11" and return -1 which confuses the sort
        if (String.IsNullOrEmpty(suffix1)) return (String.IsNullOrEmpty(suffix2)) ? 0 : -1;
        else if (String.IsNullOrEmpty(suffix2)) return 1;

        // the following shell ensure that a letter comes "before" any other special char (such as "-" or "/")
        if (Char.IsLetter(suffix1.FirstOrDefault()) && !Char.IsLetter(suffix2.FirstOrDefault())) return -1;
        else if (!Char.IsLetter(suffix1.FirstOrDefault()) && Char.IsLetter(suffix2.FirstOrDefault())) return 1;
        // if we have more complexity (compare 11a-1 with 11a-2) we use standard string-comparison
        var result = String.Compare(suffix1, suffix2);

        // if the suffixes are equal sort on the number of spaces between prefix and suffix
        if (result == 0) {
            return (spaces1 - spaces2) <0 ? -1 : (spaces1 == spaces2) ? 0: 1;
        }
        return result;
    }

    /// <inheritDoc/>
    public int Compare(string lbz1, string lbz2)
    {
        return this.compareHouseNumbers(lbz1, lbz2);
    }
}

关于c# - 对字符串列表(门牌号)进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27942444/

相关文章:

c# - 当消息传输到错误队列时,如何 Hook NServiceBus 以写入日志文件?

c# - 如何从PDF中获取文本的字体名称?

c# - 如何使用按位运算符和枚举生成动态表达式?

javascript - 数组按两次分组并计算出现次数

java - 如何对 LinkedList<String> 进行排序?

python - 如何按降序对 Python 2.7 中的任何列表进行排序?

c# - 如何获取 "module could not be found"错误对话框的文件名

c# - 获取媒体 url,包括服务器部分

java - 在Java中使用ArrayList按升序插入对象

algorithm - 排序算法的比较