1

I have a String array which contains file names like

 1.Script_DBScript_03122014

I want to sort this array so i have written the following code:

Array.Sort(SQLScripts);

But the sorted array is produced like:

 1.Script_DBScript(otherdetails)_03122014
 10.Script_DBScript(otherdetails)_03122014
 11.Script_DBScript(otherdetails)_03122014
 12.Script_DBScript(otherdetails)_03122014
 ...
 2.Script_DBScript(otherdetails)_03122014
 20.Script_DBScript(otherdetails)_03122014
 21.Script_DBScript(otherdetails)_03122014
 22.Script_DBScript(otherdetails)_03122014
 ... so on

How can i get the array in following form

 1.Script_DBScript(otherdetails)_03122014
 2.Script_DBScript(otherdetails)_03122014
 3.Script_DBScript(otherdetails)_03122014
 4.Script_DBScript(otherdetails)_03122014
 5.Script_DBScript(otherdetails)_03122014
 ...
 50.Script_DBScript(otherdetails)_03122014

EDIT : Code for retrieving filenames from the directory:

String[] SQLScripts = Directory.GetFiles(txtPath.Text, "*.sql");
Bhupendra Shukla
  • 3,814
  • 6
  • 39
  • 62
  • Strings are sorted alphabetically by default. This is sometimes worked around by adjusting the filenames, e.g. `0001.Script_DBScript_03122014`. This is also why some filenames use a dateformat like YYYYMMDD, i.e. 20140313 for March 13 2014, or even 20140313_145701 for 2:57:01 pm on that same date. – Peter Mar 12 '14 at 09:14
  • possible duplicate of [Natural Sort Order in C#](http://stackoverflow.com/questions/248603/natural-sort-order-in-c-sharp) – Oliver Mar 12 '14 at 10:03
  • for some implementations take a look at [Sorting for Humans: Natural Sort Order](http://blog.codinghorror.com/sorting-for-humans-natural-sort-order/) – Oliver Mar 12 '14 at 10:04

3 Answers3

3

Result you are getting is pretty common string sorting. What you need is digital (or numeral) sorting.

It the case, you might want to parse your strings, extract the number from the left of the first point, use some method like int.Parse("string containing a number") and then sort by integral numbers.

I'd do it like this, however I do not recommend copy paste my code without understanding what you do:

SQLScripts = SQLScripts
    .OrderBy(T => int.Parse(T.Split('.')[0]))
    .ToArray();
AgentFire
  • 8,944
  • 8
  • 43
  • 90
3

You need what's called a "natural sort order".

There is actually a native Windows API comparer called StrCmpLogicalW() which you can use to do this, calling it via P/Invoke.

You can use it to write a general purpose string comparer which you can then pass to Array.Sort(). This is useful in many cases, so it is a candidate for adding to a class library.

Here's a complete compilable example:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;

namespace ConsoleApp1
{
    [SuppressUnmanagedCodeSecurity]
    internal static class NativeMethods
    {
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
        public static extern int StrCmpLogicalW(string psz1, string psz2);
    }

    public sealed class NaturalStringComparer: IComparer<string>
    {
        public int Compare(string a, string b)
        {
            return NativeMethods.StrCmpLogicalW(a, b);
        }
    }

    sealed class Program
    {
        void run()
        {
            string[] array =
            {
                "1.Script_DBScript_03122014",
                "10.Script_DBScript_03122014",
                "11.Script_DBScript_03122014",
                "12.Script_DBScript_03122014",
                "2.Script_DBScript_03122014",
                "20.Script_DBScript_03122014",
                "21.Script_DBScript_03122014",
                "22.Script_DBScript_03122014"
            };

            Array.Sort(array); // Sorts in the wrong order.

            foreach (var filename in array)
                Console.WriteLine(filename);

            Console.WriteLine("\n");

            Array.Sort(array, new NaturalStringComparer()); // Sorts correctly.

            foreach (var filename in array)
                Console.WriteLine(filename);
        }

        static void Main(string[] args)
        {
            new Program().run();
        }
    }
}

However, this all become unnecessary if you just change the format of your strings to add leading zeroes to all the numeric parts.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
2

You have to split the strings by . and parse the numerical part to int, then you can sort numerically.

You can use LINQ:

SQLScripts = SQLScripts
  .Select(str =>  {
      string[] split = str.Split('.');
      string numberPart = split[0].Trim();
      int i;
      bool isNumber = int.TryParse(numberPart, out i);
      int? number = isNumber ? i : (int?)null;
      return new { str, split, numberPart, number };
  })
  .OrderByDescending(x => x.number.HasValue)
  .ThenBy(x => x.number)
  .Select(x => x.str)
  .ToArray();

Explanation:

This query selects an anonymous type with all relevant informations first, then it orders by the Nullable<int>.HasValue property which is a boolean indicating whether the first token could be parsed to int or not. Since true is "higher" than false i've used OrderByDescending because i want the non-parsable strings to be at the bottom.

After that(ThenBy) it will be ordered by the number itself to get the numerical order instead of the lexicographical. The last steps are to select the string from the anonymous type and to use ToArray to get the new, ordered string[].

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • The code executed with no error but the array is not sorted correctly, still got same sorted output. – Bhupendra Shukla Mar 12 '14 at 09:21
  • @RazoR: it was tested with your sample strings and sorted correctly: `1.Script_DBScript_03122014, 2.Script_DBScript_03122014, 10.Script_DBScript_03122014, 11.Script_DBScript_03122014, 12.Script_DBScript_03122014, 20.Script_DBScript_03122014, 21.Script_DBScript_03122014, 22.Script_DBScript_03122014, ...` – Tim Schmelter Mar 12 '14 at 09:28
  • Actually thr DBScript name is changed in every index – Bhupendra Shukla Mar 12 '14 at 09:31
  • @RazoR: It makes no difference. Are the numbers at the beginning really there or are they added by you to give an index? – Tim Schmelter Mar 12 '14 at 09:32