Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort array list by numbers then by letters

Tags:

c#

sorting

I have array list of strings:

"1A", "12A", "12B", "6", "A", "5B", "B", "13".

If I do myList.Sort(); then I get:

"1A", "12A", "12B", "13", "5B", "6", "A", "B".

But what I need is first sort by numbers in front, then by letter:

"1A", "5B", "6", "12A", "12B", "13", "A", "B".

I could use

public class CustomComparer : IComparer
{
    Comparer _comparer = new Comparer(System.Globalization.CultureInfo.CurrentCulture);

    public int Compare(object x, object y)
    {
        // Convert string comparisons to int
        return _comparer.Compare(Convert.ToInt32(x), Convert.ToInt32(y));
    }
}

But it throws exception. How do I get what I need?

like image 710
babboon Avatar asked May 14 '15 11:05

babboon


People also ask

How do I sort an array alphabetically?

To sort the ArrayList, you need to simply call the Collections. sort() method passing the ArrayList object populated with country names. This method will sort the elements (country names) of the ArrayList using natural ordering (alphabetically in ascending order).

How do you sort a list in an array?

Approach: An ArrayList can be Sorted by using the sort() method of the Collections Class in Java. This sort() method takes the collection to be sorted as the parameter and returns a Collection sorted in the Ascending Order by default.

Do Numbers sort before letters Java?

Java's default sorting The mapping of characters to code points is available on Unicode site . General rule is that numbers come before capital letters, and capital letters come before lowercase letters.


2 Answers

Your comparer is too simplistic. Your comparison needs to split each value into the number and the rest, then compare the numbers first, then the strings if they're equal. So it would be something like:

public sealed class NumberStringComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return NumberString.Parse(x).CompareTo(NumberString.Parse(y));
    }


    private struct NumberString : IComparable<NumberString>
    {
        private readonly int? number;
        private readonly string text;

        private NumberString(int? number, string text)
        {
            this.number = number;
            this.text = text;
        }

        internal static NumberString Parse(string text)
        {
            // TODO: Find where the digits stop, parse the number
            // (if there is one), call the constructor and return a value.
            // (You could use a regular expression to separate the parts...)
        }

        public int CompareTo(NumberString other)
        {
            // TODO: Compare numbers; if they're equal, compare
            // strings
        }
    }
}

If you have problems with either of the TODOs (after spending some time trying), you can ask for more specific help - but this is the general approach I'd use.

like image 60
Jon Skeet Avatar answered Oct 20 '22 17:10

Jon Skeet


You cannot simply pass a string "1A" or "5B" to Convert.ToInt32(x), because it has a portion that is not part of an Int32.

Instead, you should first split the string into two parts - the digits and everything else, and do the comparison with tie breaks.

One way of doing it would be writing two helper methods, and then using LINQ's OrderBy and ThenBy:

static int ExtractPrefix(string s) {
    // Parse the digits and stop; return the number
}
static string ExtractSuffix(string s) {
    // Skip digits, and return everything else
}
...
var sorted = unsorted.OrderBy(ExtractPrefix).ThenBy(ExtractSuffix).ToList();
like image 44
Sergey Kalinichenko Avatar answered Oct 20 '22 17:10

Sergey Kalinichenko