7

I have a filenames array, I want to sort it by numeric style, please give to me a solution.

Example1:

Original array: [name99.txt, name98.txt, name100.txt]
Sorted array: [name98.txt, name99.txt, name100.txt]
(Using string sorting, result of sorting is [name100.txt, name98.txt, name99.txt])

Example2:

Original array: [a99.txt, b98.txt, b100.txt]
Sorted array: [a99.txt, b98.txt, b100.txt]
(Using string sorting, result of sorting is [a99.txt, b100.txt, b99.txt])

3
  • 4
    What have you tried? Obviously it will sort alphanumeric by default, but have you tried writing a custom comparer, for example? Commented Jul 17, 2011 at 11:22
  • Your question is confusing because you don't explain what you get and what you want to get. What is “sorted array”, what is “result of sorting using string sorting”? Communicate your thoughts clearly, and you'll get good answers. Commented Jul 17, 2011 at 11:50
  • The name for this is natural sort Commented Dec 30, 2024 at 21:06

4 Answers 4

12
string[] ar = new string[] { "name99.txt", "name98.txt", "name100.txt" };
Array.Sort(ar, (a, b) => int.Parse(Regex.Replace(a, "[^0-9]", "")) - int.Parse(Regex.Replace(b, "[^0-9]", "")));

foreach (var a in ar)
    Console.WriteLine(a);

The above assumed that your files are allways called name###.txt. For the real numeric sorting use the following more complicated version:

public static void NumericalSort(string[] ar)
{
    Regex rgx = new Regex("([^0-9]*)([0-9]+)");
    Array.Sort(ar, (a, b) =>
    {
        var ma = rgx.Matches(a);
        var mb = rgx.Matches(b);
        for (int i = 0; i < ma.Count; ++i)
        {
            int ret = ma[i].Groups[1].Value.CompareTo(mb[i].Groups[1].Value);
            if (ret != 0)
                return ret;

            ret = int.Parse(ma[i].Groups[2].Value) - int.Parse(mb[i].Groups[2].Value);
            if (ret != 0)
                return ret;
        }

        return 0;
    });
}

static void Main(string[] args)
{
    string[] ar = new string[] { "a99.txt", "b98.txt", "b100.txt" };

    NumericalSort(ar);

    foreach (var a in ar)
        Console.WriteLine(a);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Great Petar! But, what you think about my "Example 2"
Just posted an implementation of the true numeric sorting - it will cover your second example.
Just noting that in production code, I would extract this in a separate method (and class, probably). It is not immediately obvious what it does, at all.
@Dan There's just no pleasing some people. First you complain about your inability to look things up on MSDN. Now you are whining about not being able to understand a .net implementation.
@David: Yeah, I'm a picky type. Sometimes this leads to answers being improved, which is the purpose of comments on StackExchange, I suppose. In my opinion, a perfect answer would contain an implementation of IComparer called FilenameComparer with this code inside. This would prove both easily maintainable and stable.
4

There may well be a managed way to do this, but I would probably just P/invoke to StrCmpLogicalW.

[DllImport("shlwapi.dll", CharSet=CharSet.Unicode, ExactSpelling=true)]
static extern int StrCmpLogicalW(String x, String y);    

If you use this function, rather than rolling your own comparison function, you'll get the same behaviour as Explorer and other system components that use logical comparison.

Note, however, that this will not work in environments where WinAPI is inaccessible (such as Windows Phone, Mono or Silverlight), might work differently on different systems and should be decorated with a comment so the future maintainer of your code knows why P/Invoke is used for sorting.

7 Comments

Nice. didn't know that call existed.
I wouldn't use WinAPI for string comparison. Firstly, it ties code to a very particular platform (I'm not even talking Mono here—think Silverlight, Windows Phone). Secondly, we are not even interoperating with Windows. I understand calling WinAPI for handling complex corner cases with window control drawing in Windows Forms, or doing some real low-level stuff. But come on, compare strings with WinAPI? As a .NET developer, I have no idea what StrCmpLogicalW does and even if shlwapi.dll exists on all systems we want target.
What I'm basically saying is, by using this call you obligate future developers to spend 10 minutes of their time researching WinAPI documentation (btw, it's in C) and more time debugging it when something does go wrong (e.g. function result might depend on Regional Settings, the library may not exist on some systems, et cetera).
@David: sure. I just wanted to point out what I think is wrong with this approach.
why not add those points in an edit to the answer. That's just a bit more constructive than a downvote.
|
1

One solution can be found here: Alphanumeric Sorting

1 Comment

Alphanumeric sorting is the norm... I could be wrong, but I think that is the wrong term here.
1

My approach:

private string[] NumericalSort(IEnumerable<string> list)
{
    var ar = list.ToArray();
    int maxPadCount = list.ToList().Max(x=> x.Length); // 9

    Array.Sort(ar, (a, b) =>
    {
        var aa = Regex.Replace(a, @"\d+", m => m.Value.PadLeft(maxPadCount , '0'));
        var bb = Regex.Replace(b, @"\d+", m => m.Value.PadLeft(maxPadCount , '0'));
        
        return string.Compare(aa, bb);
    });

    return ar;
}

1 Comment

int maxPadCount = list.ToList().Max(x=> x.Length); if you replace maxPadCount with this. max 9 numeric Char limit is also handled. m.Value.PadLeft(maxPadCount , '0')

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.