Brian B.
Brian B.

Reputation: 99

Re-order elements in a string array if they match another string?

I am trying to re-order strings in an array, based on if they match a string value.

My program is getting a list of files in a directory, and then renaming and moving the files. But I need certain files with a specific name to be renamed after other files. (the files are being renamed with time stamps and are processed in that order).

Example File names:

File-302.txt
File-302-IAT.txt
File-303.txt
File-303-IAT.txt
File-304.txt
File-304-IAT.txt
File-305.txt
File-305-IAT.txt

Basically what I am trying to accomplish, is I would like to move all the files containing "-IAT" to the end if the array, so that when I loop through, the IAT files are processed after their non "IAT" partner file.

Here is my code but theres not much to it:

string[] outFiles = Directory.GetFiles(workingDir);

for (int i = 0; i <= outFiles.Length - 1; i++
{
    //code to rename the file in the array
}

Upvotes: 0

Views: 222

Answers (3)

Brian B.
Brian B.

Reputation: 99

Actually, what I ended up doing was just a simple bubble sort, as the amount of files i am dealing with is very small. I changed from storing the files in an array to a list.

List<string> outFiles = new List<string>(Directory.GetFiles(workingDir));
bool noSort;

do
{
    noSort = true;
    for (int i = 0; i <= outFiles.Count - 2; i++)
    {
        if (outFiles[i].EndsWith("IAT.TXT"))
        {
            if (!outFiles[i + 1].EndsWith("IAT.TXT"))
            {
                string temp = outFiles[i + 1];
                outFiles[i + 1] = outFiles[i];
                outFiles[i] = temp;
                noSort = false;
            }
        }
    }
}
while (noSort == false);

Upvotes: 0

Martin Liversage
Martin Liversage

Reputation: 106826

You can use a custom IComparer<string> implementing your sorting rule:

class IatEqualityComparer : IComparer<string>
{
    public int Compare(string a, string b)
    {
        if (a == b)
            return 0;
        var aWithoutExtension = Path.GetFileNameWithoutExtension(a);
        var bWithoutExtension = Path.GetFileNameWithoutExtension(b);
        var aIsIat = aWithoutExtension.EndsWith("IAT", StringComparison.InvariantCultureIgnoreCase);
        var bIsIat = bWithoutExtension.EndsWith("IAT", StringComparison.InvariantCultureIgnoreCase);
        if (aIsIat && !bIsIat)
            return 1;
        if (!aIsIat && bIsIat)
            return -1;
        return a.CompareTo(b);
    }
}

(In Windows file names are not case sensitive so you have to be very careful when you look for a specific pattern like IAT in a file name. It will almost always work as expected except for that one time in production where the file ended with iat and not IAT...)

You can then sort an array using Array.Sort:

Array.Sort(outFiles, new IatEqualityComparer());

This will sort the array in place. The result is:

File-302.txt
File-303.txt
File-304.txt
File-305.txt
File-302-IAT.txt
File-303-IAT.txt
File-304-IAT.txt
File-305-IAT.txt

The IComparer<string> can also be used when sorting lists in place and with LINQ OrderBy.

Upvotes: 4

spender
spender

Reputation: 120450

If you project your items into a new sequence with two different ordering fields, you can then use LINQ to sort the projection accordingly, then extract the file name from the resulting sequence:

outFiles
    .Select(fn => new{
        order = Path.GetFileNameWithoutExtension(fn).EndsWith("-IAT") ? 1 : 0,
        fn
    })
    .OrderBy(x => x.order)
    .ThenBy(x => x.fn)
    .Select(x => x.fn)

Upvotes: 1

Related Questions