LaserBeak
LaserBeak

Reputation: 3285

C# Sorting a FileInfo Array by Name, getting some unexpected results and need to know if I have other sorting options available

I am saving my image files in the following format: 1-6784 (1 being the sort order that I want to see and 6784 being a randomly generated number between 1000 and 9999.

When I look at the folder through explorer and arrange by name they all look fine and sorted according to the first number i.e. (1-XXXX, 2-XXXX , 9-XXXX, 12-XXXX etc.) in ascending order.

However when I get the FileInfo array for this directory it automatically sorts it by name I presume, but it for some reason would place 10-XXXX and 11-XXXX before 1-XXXX, 2-XXXX etc. So up until 10 it's fine and the order retains when image src links are generated in the view in my web application, but once I upload\save more than 9 files the double digit 10, 11 etc. take front spots in the array over the single digit numbers.

DirectoryInfo sourceDir = new DirectoryInfo(System.Web.HttpContext.Current.Request.MapPath("~/Content/ProductImages/" + Model.Products[i].ProductID.ToString() + "/thumbs/"));

                   if (sourceDir.Exists)
                   {

                       FileInfo[] fileEntries = sourceDir.GetFiles();
                       Array.Sort(fileEntries, (f1, f2) => f1.Name.CompareTo(f2.Name));


}

Upvotes: 1

Views: 4702

Answers (6)

dana
dana

Reputation: 18155

You could parse out the numbers and compare them as follows:

DirectoryInfo sourceDir = new DirectoryInfo(System.Web.HttpContext.Current.Request.MapPath("~/Content/ProductImages/" + Model.Products[i].ProductID.ToString() + "/thumbs/"));

if (sourceDir.Exists)
{
    FileInfo[] fileEntries = sourceDir.GetFiles();
    Array.Sort(fileEntries,
        delegate(FileInfo x, FileInfo y)
        {
            String[] xvals = x.Name.Split('-');
            String[] yvals = y.Name.Split('-');

            int cmp = Int32.Parse(xvals[0]).CompareTo(Int32.Parse(yvals[0]));
            if (cmp != 0)
            {
                return cmp;
            }

            cmp = Int32.Parse(xvals[1]).CompareTo(Int32.Parse(yvals[1]));
            return cmp;
        }
    );
}

Upvotes: 1

dknaack
dknaack

Reputation: 60556

Description

You sort a string and this is the right result.

That is lexicographic sorting which means basically the language treats the variables as strings and compares character by character ("200" is greater than "19999" because '2' is greater than '1') ...

Source Why do some sorting methods sort by 1, 10, 2, 3…?

Solution

I suggest you create a custom Comparer which pads leading zeros to the fileName.

public class MyCustomComparer : IComparer<FileInfo>
{
    public int Compare(FileInfo x, FileInfo y)
    {
        // split filename
        string[] parts1 = x.Name.Split('-');
        string[] parts2 = y.Name.Split('-');

        // calculate how much leading zeros we need
        int toPad1 = 10 - parts1[0].Length;
        int toPad2 = 10 - parts2[0].Length;

        // add the zeros, only for sorting
        parts1[0] = parts1[0].Insert(0, new String('0', toPad1));
        parts2[0] = parts2[0].Insert(0, new String('0', toPad2));

        // create the comparable string
        string toCompare1 = string.Join("", parts1);
        string toCompare2 = string.Join("", parts2);

        // compare
        return toCompare1.CompareTo(toCompare2);
    }
}

And call them

FileInfo[] fileEntries = sourceDir.GetFiles();
Array.Sort(fileEntries, new MyCustomComparer());

More Information

Upvotes: 4

the_joric
the_joric

Reputation: 12261

You need to pad your sort order with leading zeros:

0001-XXX
0002-XXX
...

Or you may try the following to sort your exiting files:

FileInfo[] fileEntries = sourceDir.GetFiles()
   .OrderBy(f => Regex.Match(f.Name, "^[0-9]+").Value.PadLeft(10, '0'))
   .ToArray();

Upvotes: 3

Sir Crispalot
Sir Crispalot

Reputation: 4854

If you don't want to change your file names, you could do the padding during the comparison. I chose to pad it to ten numbers, but it may need to be more depending on how many files you have.

Array.Sort(fileEntries, (f1, f2) => f1.Name.PadLeft(10, '0').CompareTo(f2.Name.PadLeft(10, '0'));

Upvotes: 0

Alexey Raga
Alexey Raga

Reputation: 7545

The reason is clear: the file name is a string, and as a string "11" comes after "1" but before "2".

In order to do what you want, in your comparison function you will need to parse the name and compare parts (separated by dash) as numbers (simply cast and compare)

Upvotes: 1

spender
spender

Reputation: 120518

How about pre-padding your numbers with zeroes, so that they are fixed length. This way alphanumeric comparison will yield the same result as the numeric comparison you are expecting.

Upvotes: 0

Related Questions