对一个字符串列表(房屋编号)进行排序。

3

我有一个字符串列表(包含房号),如下所示:

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”开头的后缀排在没有“a”的后缀之前?
如果有更优雅的方法来实现这一点,我会欣然接受任何改进意见。
编辑:将输入拆分为实际数字和后缀大多使用此正则表达式完成 (\d+)\s*(\S*)。这将导致整数部分(实际房屋号码)和该数字后面的字符串部分。之后,我们仅通过使用 suffix1 = suffix1.Trim(' ', '-', '/');(suffix2相应地)修剪任何非字母数字字符。话虽如此,我们实际上并没有将-1与a-1进行比较,而是将1与a-1进行比较。但是,这对结果本身没有任何影响(因为-1和1都按字典顺序小于a)。
编辑2:我消除了列表中的一些成员,以便仅剩下两个有问题的成员:“List myList = new List {“11-1”,“11a-1”}; "。更改后,排序结果如预期的那样:“{“11a-1”,“11-1”}”。
编辑3:我刚刚改变了列表中成员的顺序(将11放在列表的末尾)。现在结果也是预期的。因此,它似乎取决于列表中元素的初始顺序。非常奇怪...

3
"11a-1"比"11-1"在顺序上进一步往下,因为"-"的字符值比"a"低。请查看ASCII表格。您可能需要定义自定义字符顺序。 - S_F
当你比较后缀“-1”和“a-1”时,“-1”会排在前面,因为它更小;而且它更小是因为减号的ASCII码比字母“a”的码小。 - CiaPan
这就是为什么我引入了这个中间步骤,检查第一个字符是否是字母... 因此 11-1 将输给 11a-1,不是吗? - MakePeaceGreatAgain
(因为“-1”和“1”在字典顺序上都比“a”小。)- 我的英语有限,无法确定您的意思,但在这种情况下重要的是ASCII表,其中“-”和“1”都在“a”之前。 - S_F
@S_F 在这种情况下不相关,因为代码解决了“ - ”不是字母并覆盖了匹配的问题。 实际问题是快速排序算法执行的“11”和“11”的比较使用此代码返回“-1”,而不是所需的“0”,从而产生不可预测的结果。 - Bob Vale
显示剩余4条评论
1个回答

0
尝试以下内容,由于您没有给出如何分割前缀和后缀的示例,所以我编了一个示例:
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);
    }
}

请注意,此处没有空值检查,您可能需要添加。 - Bob Vale
虽然你猜对了如何拆分代码,但你的解决方案似乎并没有为我的帖子增加任何内容(除了最后检查结果)。 - MakePeaceGreatAgain
@HimBromBeere 你是对的,我是个白痴,我误读了我的测试结果,让我来修复它。 - Bob Vale
1
@HimBromBeere 找到了问题,只需修复示例即可。问题在于如果两个后缀都为空,则不返回0。 - Bob Vale
确实,就是这样...我把它改成了 if (suffix1 == suffix2) return 0(更短的方式),现在它可以工作了。非常感谢。 - MakePeaceGreatAgain
@HimBromBeere 没问题,我还修复了空格代码,以便根据需要返回 -1、0 或 1,因此您也可以按照所需的方式对 "11 a" 进行排序。 - Bob Vale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接