2

I'm having a List<String> l_lstTemp and which contains

"A1"
"A1_1"
"A1_2"
"1A"
"B2_1"
"B1_2"
"B1_1_2"
"A10"
"B11"
"A"
"Z"

I need to sort the items based on the character and numeric value.

So the sorted list will be like

"1A"
"A"
"A1"
"A1_1"
"A1_2"
"A10"
"B1_1_2"
"B1_2"
"B2_1"
"B11"
"Z"

Here is my code:

l_lstTemp.Sort(delegate(String One, String Two)
{
    Match l_mOne = Regex.Match(One, @"(\D*)(\d*)");
    Match l_mTwo = Regex.Match(Two, @"(\D*)(\d*)");
    int Result;
    if (l_mOne.Success || l_mTwo.Success)
    {
        String l_strX, l_strY;
        l_strX = l_mOne.Groups[1].Value;
        l_strY = l_mTwo.Groups[1].Value;
        Result = l_strX.CompareTo(l_strY);
        if (Result != 0)
            return Result;
        l_strX = l_mOne.Groups[2].Value;
        l_strY = l_mTwo.Groups[2].Value;
        if (l_strX == String.Empty || l_strY == String.Empty)
        {
            Result = l_strX.CompareTo(l_strY);
            if (Result != 0)
                return Result;
        }
        else
        {
            long X = long.Parse(l_strX);
            long Y = long.Parse(l_strY);
            Result = X.CompareTo(Y);
            if (Result != 0)
                return Result;
        }

    }
    return 0 ;
}
);

But its not working (sorting) properly.

How do I modify my code to sort the list properly?

Please post me a way to do this.

Thanks in advance.

2
  • Adding regex tag as the question is more about that than it is about sorting. Commented May 11, 2011 at 5:56
  • You know, you don't even have to pass a delegate, the framework will use a default string comparer that will give you the result you ask for. Unless you've stated your problem the wrong way? Commented May 11, 2011 at 5:58

3 Answers 3

3

I did some modifications to your code. The thing was that when both Group 1 and Group 2 are equals, you still need to check what remains.

Important: I did the modifications inside your code, so this could be a little tricky. I really suggest you refactoring your code now that you know it works:

l.Sort(delegate(String One, String Two)
{
    while (One != "" && Two != "")
    {
        if (One == Two)
            return 0;

        //Add one more group to capture what remains of the expression
        Match l_mOne = Regex.Match(One, @"_*(\D*)(\d*)(.*)$"); 
        Match l_mTwo = Regex.Match(Two, @"_*(\D*)(\d*)(.*)$");
        int Result;
        if (l_mOne.Success || l_mTwo.Success)
        {
            String l_strX, l_strY;
            l_strX = l_mOne.Groups[1].Value;
            l_strY = l_mTwo.Groups[1].Value;
            Result = l_strX.CompareTo(l_strY);
            if (Result != 0)
                return Result;
            l_strX = l_mOne.Groups[2].Value;
            l_strY = l_mTwo.Groups[2].Value;
            if (l_strX == String.Empty || l_strY == String.Empty)
            {
                Result = l_strX.CompareTo(l_strY);
                if (Result != 0)
                    return Result;
            }
            else
            {
                long X = long.Parse(l_strX);
                long Y = long.Parse(l_strY);
                Result = X.CompareTo(Y);
                if (Result != 0)
                    return Result;
                One = l_mOne.Groups[3].Value; //Store in 'One' the remaining part of the regex
                Two = l_mTwo.Groups[3].Value; //The same in Two

                continue; //The result will be the result of the comparison of those two new values.
            }

        }
    }
    return One.CompareTo(Two);
});

Edit:
I also added _* to remove all the _ characters from the begining of the strings. I assumed here that the strings will only contain _ after the numbers and not something like B1B or B1$.

The thing here is that you don't really explain how the comparison should be made, and I had to assume those things from your original data and the sorted data, otherwise what would happen if you want to sort A1A and A1_? What should it return?

Sign up to request clarification or add additional context in comments.

Comments

1

Here's how I would implement such a comparer. Much easier to follow IMHO.

var re = new Regex(@"^(\d+)?([A-Z]+)(\d+)?(?:_(\d+)(?:_(\d+))?)?$");
Func<Group, int, int> intOrDefault = (g, d) => g.Success ? Convert.ToInt32(g.Value) : d;
list.Sort((x, y) =>
{
    var xm = re.Match(x);
    var ym = re.Match(y);
    int cmp;

    // compare the first group
    // compare the leading numbers (if any)
    cmp = intOrDefault(xm.Groups[1], int.MaxValue).CompareTo(intOrDefault(ym.Groups[1], int.MaxValue));
    if (cmp != 0)
        return cmp;
    // compare letters
    cmp = xm.Groups[2].Value.CompareTo(ym.Groups[2].Value);
    if (cmp != 0)
        return cmp;
    // compare the trailing numbers (if any)
    cmp = intOrDefault(xm.Groups[3], 0).CompareTo(intOrDefault(ym.Groups[3], 0));
    if (cmp != 0)
        return cmp;

    // compare the next group
    cmp = intOrDefault(xm.Groups[4], 0).CompareTo(intOrDefault(ym.Groups[4], 0));
    if (cmp != 0)
        return cmp;

    // compare the last group
    cmp = intOrDefault(xm.Groups[5], 0).CompareTo(intOrDefault(ym.Groups[5], 0));
    return cmp;
});

Comments

-3

For this example, just calling sort l_lstTemp.Sort() would get you the result you are looking for

3 Comments

If i use the Sort method for A1, A2, A11. then it will be sorted like A1, A11, A2.
But your sampler data did not include "A11"
I mentioned A11 for just example. The same will happen for B1_1 and B11(In my qn)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.