【问题标题】:sort a list of strings(housenumbers)对字符串列表(门牌号)进行排序
【发布时间】:2015-01-14 21:14:27
【问题描述】:

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

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" 开头的后缀?

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

编辑:将输入拆分为实际数字和后缀主要是使用此正则表达式(\\d+)\\s*(\\S*) 完成的。这会产生一个整数部分(实际的 housnumber)和该数字后面的字符串部分。之后,我们只使用suffix1 = suffix1.Trim(' ', '-', '/');(适当地使用suffix2)从后缀中修剪任何非字母数字字符。话虽如此,我们实际上并未将-1a-1进行比较,而是将1a-1进行比较。然而,这并不会改变结果本身的任何内容(因为-11 都小于a)。

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

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

【问题讨论】:

  • “11a-1”的顺序比“11-1”更靠后,因为“-”的字符值比“a”低。看一下 ASCII 表。您可能需要以某种方式定义您的自定义字符顺序。
  • 当你比较后缀 "-1""a-1" 时,'"-1"' 排在第一位,因为它更小;并且它更小,因为减号的 ASCII 代码小于 'a' 的代码。
  • 这就是为什么我引入了这个中间步骤来检查第一个字符是否为字母......因此11-1 会输给11a-1,不是吗?
  • @HimBromBeere 很难说,因为我们看不出你到底是如何分割字符串的。
  • @S_F 看到我的编辑,我希望它能澄清事情。

标签: c# sorting


【解决方案1】:

试试下面的,你没有给出如何拆分前缀和后缀的例子,所以我做了一个:

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);
    }
}

【讨论】:

  • 请注意这里没有空值检查,您可能想要添加它
  • 虽然您猜得很好,但拆分解决方案的代码似乎并没有在我的帖子中添加任何内容(除了最后一次检查您的结果)。
  • @HimBromBeere 你说得对,我是个白痴,我错过了我的测试结果,让我修复它
  • @HimBromBeere 发现只是修复示例的问题,问题是如果两个后缀都为空,则不会返回 0
  • 确实,就是这样...我将其更改为if (suffix1 == suffix2) return 0(更短),现在它可以工作了。非常感谢。
猜你喜欢
  • 2015-03-26
  • 2021-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-15
  • 2010-09-07
相关资源
最近更新 更多