C#中的自定义字符串比较

12

我想在C#中实现一个自定义的字符串比较器IComparer并将其应用于一个下拉框ComboBox

实际结果

如果我将ComboBoxSorted属性设置为true,输出如下:

A
AA
AAA
B
BB
BBB

期望结果

排序算法的期望行为如下(金融开发人员将会明白为什么 :)):

AAA
AA
A
BBB
BB
B

问题

这个能做到吗?需要排序算法吗?

PS:我不需要完整的代码答案,只是需要一个大概的思路...

编辑

这是关于信用评级的。在我的问题中我遗漏了一些内容。这些评级必须按照以下顺序排序:

XXX
XX+
XX
XX-
X+
X
X-

带有 X in ('A','B','C')'A' > 'B' > 'C' 的条件。


2
ComboBox在msdn上的文档中指出:“排序是不区分大小写的,并按字母顺序升序排列。” 我认为你最好的选择是以正确的顺序“插入”元素。(如果它是可枚举的,你可以通过在元素集合上调用OrderBy来实现)。 - Benjamin Gruenbaum
将列表分成子列表并按长度排序可能是一个选项。 - jAC
@Damien_The_Unbeliever 这只是一个字符串操作的问题。他只是在对一组字符串进行排序。他用它做什么并不重要。 - Servy
@Damien_The_Unbeliever OP明确提到了“IComparer”,因此无论他在使用时做什么,都可以利用该接口(正如我所预期的那样)。拥有接口的整个目的在于,在实现它时,您不需要关心它被消耗的方式,您只需遵守接口契约即可。 - Servy
2
他可能会这样做,保持Sorted属性为false,然后如果他有一个叫做arr的string[]数组,可以使用Array.Sort(arr, comparer)方法对该数组进行排序,然后通过使用其中一个答案中提供的自定义比较器来添加字符串,最后使用hisComboBox.Items.AddRange(arr)方法添加。@BenjaminGruenbaum建议此方法。 - Jeppe Stig Nielsen
显示剩余6条评论
3个回答

6
这里是大部分实现的版本:
public class MyComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        //todo null checks on input

        var pairs = x.Zip(y, (a, b) => new { x = a, y = b });

        foreach (var pair in pairs)
        {
            int value = pair.x.CompareTo(pair.y);
            if (value != 0)
                return value;
        }


        //if we got here then either they are the same,
        //or one starts with the other
        return y.Length.CompareTo(x.Length); //note x and y are reversed here
    }
}

所以,这使用Zip从每个对应的字符串中获取字符对,直到其中一个结束为止,如果它们不相等,则返回适当的值。如果成功通过此步骤,则一个字符串以另一个字符串开头。对于传统的字符串比较,我们将按照输入参数的相同顺序进行长度比较。由于我们基本上是根据长度反转顺序,因此请注意在最后一行交换了xy。这会颠倒比较逻辑。

听起来询问者拥有一个 Windows Forms 的“ComboBox”,因为他提到了 Sorted 属性。在该文档页面上,它说排序是按字母顺序排列的。因此问题是,他如何设置他的组合框来使用您的比较器。也许他不能在他的组合框中使用字符串作为“项”?(我不记得 System.Windows.Forms.ComboBox 的所有细节。) - Jeppe Stig Nielsen
@JeppeStigNielsen OP明确要求实现一个应用指定逻辑的IComparer。如果他不确定如何将该比较器应用于他的特定情况(并且他没有表现出这个问题),那么这实际上是一个不同的问题。如果您知道设置自定义比较器很棘手,并希望在评论中链接/解释如何这样做,那么请务必这样做。 - Servy
感谢您的回答@Servy。我已经编辑了我的帖子,并考虑到我所添加的内容,您的算法似乎效率不高。 - Fares

2
假设这是关于信用评级的,通常可以在 "CreditRating" 类上添加一个“排序序列”列,然后在将其分配为下拉列表的数据源之前对列表进行排序。但是,一个快速的解决方法(基于可能的有限值)是按照首字母升序,然后按字符串长度降序排序:
if(left[0] != right[0])
    return left[0].CompareTo(right[0]);
else
    return right.Length - left.Length;

如果您想更好地控制顺序,另一个解决方法是创建一个可能值的列表,按正确顺序排列,然后使用该列表对列表进行排序:

public class MyComparer : IComparer<string>
{
    private static readonly string[] Ratings = new [] {
        "CC","C","CCC-","CCC","CCC+",
        "B-","B","B+","BB-","BB","BB+","BBB-","BBB","BBB+",
        "A-","A","A+","AA-","AA","AA+","AAA"};
    // reverse the order so that any strings not found will be put at the end.

    public int Compare(string left, string right)
    {
       return Array.IndexOf(Ratings, right).CompareTo(Array.IndexOf(Ratings, left));
    }
}

1
虽然这个方法对于给定的确切数据有效,但它无法正确地将“ABB”与“AA”排序。 “AA”应该排在前面,但是使用您的代码,它会排在第二位。 您需要遍历两个字符串中的每个字符,直到找到一个不匹配或到达末尾,然后比较长度。 您不能仅比较第一个字符。 - Servy
1
@Servy,这就是为什么我假设这些值是信用评级的原因。ABB不是有效的信用评级。 - D Stanley
感谢您的回复@DStanley。确实是针对信用评级的。但是,在我的问题中,我省略了以 +- 结尾的利率。一开始我考虑了你的算法,但是记住了我最后的话,它变得更加复杂了。 - Fares
1
在我看来,“最好”的答案仍然是为信用评级定义一个类,并添加一个用于排序的Order属性。但是,我已经添加了另一种解决方法,可以给您更多的控制。 - D Stanley
那似乎足够高效。谢谢@DStanley! - Fares

0
编写 IComparer,使其接受字符串但按字符比较。
if A[0] == B[0] go to the next character.
if B[1] == null or A[1] < B[1], return A < B.
if A[1] == null or B[1] < A[1], return B < A.
if equal...continue as needed

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