c# 按数字顺序组织字典键

c# organize dictionary keys in numerical order

我有一个程序,我按文件目录的顺序解析 sql 脚本。团队的想法是按顺序对 sql 脚本进行更改或添加,因此文件夹的名称从 0 到 12。所以我需要按数字顺序解析这些文件夹,但是当它们按顺序解析,这些是我将它们作为字典中的键放置的顺序:

C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders[=10=] 
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders  
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders 
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders 
etc...

当我遍历这本字典时,我想 运行 这些文件夹按数字顺序排列,这样我就可以按照设计的顺序构建我的 sql 脚本。我将路径保存为键(字符串),我需要重新组织它们,以便路径文件夹按数字顺序列出。所以他们看起来像这样:

C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders[=11=] 
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders  
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders 
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders 
etc...
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders

我的词典结构是<string, List<string>>的形式。我正在查看一个包含一系列文件夹的文件目录,每个文件夹包含少量 SQL 个用于构建数据库的文件。 My Dictionary Keys 是该目录中子文件夹的文件夹路径,值是一个字符串列表,其中包含 Key 中文件夹路径内的那些文件的路径。我将编辑我的问题以提及这一点。我怎样才能按数字顺序按键来订购我的字典?

尝试以下操作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] input = {
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders[=10=]",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders",
                                 @"C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders"
                             };

            Dictionary<string,List<int>> dict = input.GroupBy(x => x.Substring(0,x.LastIndexOf(@"\") + 1), y => int.Parse(y.Substring(y.LastIndexOf(@"\") + 1)))
                .ToDictionary(x => x.Key, y => y.OrderBy(z => z).ToList());

        }
    }
}

根据@Paparazzi 的要求

public class NaturalSortComparer : IComparer<string>
{
    Regex _Regex = new Regex(@"(\d+)|(\D+)");

    public int Compare(string s1, string s2)
    {
        var list1 = _Regex.Matches(s1).Cast<Match>().Select(m => m.Value.Trim()).ToList();

        var list2 = _Regex.Matches(s2).Cast<Match>().Select(m => m.Value.Trim()).ToList();

        var min = Math.Min(list1.Count, list2.Count);
        int comp = 0;

        for (int i = 0; i < min; i++)
        {
            int intx, inty;

            if (int.TryParse(list1[i], out intx) && int.TryParse(list2[i], out inty))
                comp = intx - inty;
            else
                comp = String.Compare(list1[i], list2[i]);

            if (comp != 0) return comp;
        }

        return list1.Count - list2.Count;
    }
}

现在你可以在SortedDictionaryHashSetSortedList等中使用它,也作为

foreach(var entry in dict.OrderBy(x => x.Key, new NaturalSortComparer())
{
}

PS:由于每次需要比较时都使用正则表达式,因此性能不会很好...如果需要,可以生成 cached 版本进行排序更大 列表..

所以我从一些评论中学到了很多东西,它帮助我找到了这个答案。我需要的答案取决于以下几点:

无法订购字典。要按所需顺序存储键值对,需要使用 StoredDictionary

自然排序不是一件容易处理的事情。为此,Linq 包需要与 OrderBy() 函数一起使用。

.OrderedBy() 需要使用一个 IComparer,在这种情况下,必须设计一个自定义的

我很幸运地看到一篇文章,该文章制作了一个自定义比较器,专门用于对其中包含数字的字符串进行自然排序。 A special thanks to James McCormack for this comparer.

首先,我将我的 scriptsPaths 字典变成了 SortedDictionary。我还实例化了一个 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>>();

然后,一旦我的程序解析了子目录路径的目录,我就创建了一个新的 class,命名为 NaturalSortComparer,我把从 link 以上:

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);
    }

有了自定义比较器,我现在将 .OrderBy() 与自定义比较器一起使用

var organizedPaths = directoryManager
                .ProcessDirectory(dbDirectory)
                .OrderBy(x => x.Key, new NaturalSortComparer<string>());

在 foreach 循环到 Console.WriteLine() 键之后,我得到了我想要的顺序:

C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders[=13=]
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders
C:\Dev\FileResearch\ParseSqlConsole\DbScriptFolders

最后的说明 我在 SortedDictionary 上找到的每个资源都表明它们比 Dictionary 资源密集得多,所以要小心非常大的资源那些。但是,我只能希望我的文件夹结构不会增长太多,所以这对我来说是可以接受的。