I have a program where I am parsing sql scripts in order of the file directory. The idea from the team was that changes or additions to the sql scripts were done in order, and so the folders are named from 0 to 12. So I need to parse through these folders in numerical order, however when they are parsed in order, these are the order in which I am placing them as the Key in the dictionary:
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\0
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\1
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\10
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\11
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\12
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\2
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\3
etc...
When I iterate through this dictionary, I want to run these folders in numerical order so I can build my sql scripts in the order they were designed. I have the path saved as the key (string), and I need to reorganize them so that the path folders are listed in numerical order. So that they look like this:
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\0
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\1
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\2
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\3
etc...
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\9
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\10
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\11
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\12
My Dictionary structure is in the form of <string, List<string>>
. I am looking at a file directory that holds a series of folders, and each folder holds a handful of SQL files for building a database. My Dictionary Keys are the folder paths for the subfolders inside this directory, and the values is a List of strings that hold the paths for those files inside the folder path in the Key. I'll edit my question to mention this. How can I order my dictionary by my keys in numerical order?
So I've learned a lot from some of the comments, and it helped guide me to this answer. The answer to what I needed relies on the following points:
A Dictionary cannot be ordered. To store the key value pairs in a desired order, a StoredDictionary
needs to be used
Natural Sort is not something that is handled easily. To achieve this, the Linq
package needs to be used with the OrderBy()
function.
.OrderedBy()
needs to use a IComparer
and in this case, a custom one had to be designed
I was very fortunate to come across an article that makes a custom comparer specifically for natural ordering of strings with numbers in them. A special thanks to James McCormack for this comparer.
First off, I've turned my scriptsPaths
dictionary into a SortedDictionary. And I also instantiate a organizedPaths
SortedDictionary.
public static IDictionary<string, List<string>> scriptsPaths = new SortedDictionary<string, List<string>>();
public static IDictionary<string, List<string>> organizedPaths = new SortedDictionary<string, List<string>>();
Then, once my program has parsed the directories for the subdirectory paths, I then created a new class, named NaturalSortComparer
, where I've placed the customer IComparer found from the link above:
int IComparer<string>.Compare(string x, string y)
{
if (x == y)
return 0;
string[] x1, y1;
if (!table.TryGetValue(x, out x1))
{
x1 = Regex.Split(x.Replace(" ", ""), "([0-9]+)");
table.Add(x, x1);
}
if (!table.TryGetValue(y, out y1))
{
y1 = Regex.Split(y.Replace(" ", ""), "([0-9]+)");
table.Add(y, y1);
}
int returnVal;
for (int i = 0; i < x1.Length && i < y1.Length; i++)
{
if (x1[i] != y1[i])
{
returnVal = PartCompare(x1[i], y1[i]);
return isAscending ? returnVal : -returnVal;
}
}
if (y1.Length > x1.Length)
{
returnVal = 1;
}
else if (x1.Length > y1.Length)
{
returnVal = -1;
}
else
{
returnVal = 0;
}
return isAscending ? returnVal : -returnVal;
}
private static int PartCompare(string left, string right)
{
int x, y;
if (!int.TryParse(left, out x))
return left.CompareTo(right);
if (!int.TryParse(right, out y))
return left.CompareTo(right);
return x.CompareTo(y);
}
With the custom comparer in place, I now used the .OrderBy()
with the custom comparer
var organizedPaths = directoryManager
.ProcessDirectory(dbDirectory)
.OrderBy(x => x.Key, new NaturalSortComparer<string>());
After a foreach loop to Console.WriteLine() the keys, I get the order in which I wanted:
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\0
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\1
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\2
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\3
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\4
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\5
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\6
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\7
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\8
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\9
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\10
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\11
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders\12
A final note Every resource I found on SortedDictionary
states that they are much more resource intensive than a Dictionary
, so watch out with very large ones. However, I can only hope that my folder structure will not grow much more, so this is acceptable for me.