19

I have a collection of strings in c#, for example;

var example = new string[]{"c", "b", "a", "d"};

I then with to sort this, but my IComparer method is not working, and looping infinitely by the seems of things.

Basically I need "b" to come first, followed by "c", then I dont care about the order of any of the others.

Is this possible using IComparer<string> and the Compare(string x, string y) method?

Edit: Code

    public int Compare(string x, string y)
    {
        var sOrder = new string[] { "b", "c" };
        int index_x = -1;
        int index_y = -1;

        for (int i = 0; i < sOrder.Length;i++)
        {
            if (sOrder[i] == x)
                index_x = i;
            else if (sOrder[i] == y)
                index_y = i;
        }

        if (index_x >= 0 && index_y >= 0)
        {
            if (index_x < index_y)
            {
                return -1;
            }
            else
                return 1;
        }
        return 0;
    }
maxp
  • 24,209
  • 39
  • 123
  • 201
  • Yes, it is possible. We'll need to see your code in order to tell you why it isn't working... – Cody Gray - on strike Jan 23 '12 at 17:19
  • Ok there's still a lot more information missing here. What happens to strings that do not have one character in them? Do you want to enforce this ordering on strings or individual characters? etc... – Jeff Mercado Jan 23 '12 at 17:24

2 Answers2

30

This should do what you want:

var example = new string[]{"c", "a", "d", "b"};
var comparer = new CustomStringComparer(StringComparer.CurrentCulture);
Array.Sort(example, comparer);

...

class CustomStringComparer : IComparer<string>
{
    private readonly IComparer<string> _baseComparer;
    public CustomStringComparer(IComparer<string> baseComparer)
    {
        _baseComparer = baseComparer;
    }

    public int Compare(string x, string y)
    {
        if (_baseComparer.Compare(x, y) == 0)
            return 0;

        // "b" comes before everything else
        if (_baseComparer.Compare(x, "b") == 0)
            return -1;
        if (_baseComparer.Compare(y, "b") == 0)
            return 1;

        // "c" comes next
        if (_baseComparer.Compare(x, "c") == 0)
            return -1;
        if (_baseComparer.Compare(y, "c") == 0)
            return 1;

        return _baseComparer.Compare(x, y);
    }
}
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Doesn't this do a stackoverflow? You should have wrote `if(x==y)return 0;` instead of `if (_baseComparer.Compare(x, y) == 0) return 0;`, which would result in recursion. Correct me if I am wrong! – ˈvɔlə Aug 29 '14 at 11:39
  • Nice solution. Its performances can be slightly improved avoiding to call twice ```_baseComparer.Compare(x, y)``` – NinjaCross Apr 17 '19 at 10:18
  • What if a string is empty or NULL ? – prototype0815 Oct 29 '21 at 11:40
  • @prototype0815 for the current culture comparer, null is considered less than an empty string, and an empty string is less than any non-empty string. – Thomas Levesque Nov 02 '21 at 13:13
7

A simple way is to substitute integers for the strings.

class MyComparer : IComparer<string>
{
    public override int Compare(string x, string y)
    {
        int ix = x == "b" ? 0 : x == "c" ? 1 : 2;
        int iy = y == "b" ? 0 : y == "c" ? 1 : 2;
        return ix.CompareTo(iy);
    }
}

var example = new List<string> { "c", "b", "a", "d", "foo", "", "1", "e"};
example.Sort(new MyComparer());
foreach (var s in example)
    Console.WriteLine(s);

Output:

b
c

1
e
a
d
foo

Note that this isn't a stable sort. If you need a stable sort, there's a little more work involved.

arserbin3
  • 6,010
  • 8
  • 36
  • 52
Igby Largeman
  • 16,495
  • 3
  • 60
  • 86
  • 2
    With the same comparer, Linq's `OrderBy` does a stable sort: `var ordered = example.OrderBy(s => s, new MyComparer());`. But in the case you might as well say `.OrderBy(s => s == "b" ? 0 : s == "c" ? 1 : 2)` directly (although I don't like nesting `?:` operators). – Jeppe Stig Nielsen Jul 02 '13 at 18:35