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

时间:2018-03-05 18:08:49

标签: c# sorting dictionary numerical

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

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...

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

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

我的词典结构采用<string, List<string>>的形式。我正在查看包含一系列文件夹的文件目录,每个文件夹包含一些用于构建数据库的SQL文件。我的词典键是此目录中子文件夹的文件夹路径,值是一个字符串列表,用于保存键中文件夹路径中这些文件的路径。我将编辑我的问题以提及此问题。如何按数字顺序通过我的键订购我的字典?

3 个答案:

答案 0 :(得分:2)

根据@Paparazzi的要求

#particleground

现在您可以在 SortedDictionary HashSet SortedList 等中使用它,也可以

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

PS:因为它在每次需要比较时都使用正则表达式,所以它不会非常高效...如果需要,可以生成缓存的版本用于排序更大的列表..

答案 1 :(得分:1)

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

无法订购字典。要按所需顺序存储键值对,需要使用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>>();

然后,一旦我的程序解析了子目录路径的目录,我就创建了一个名为NaturalSortComparer的新类,其中我放置了从上面的链接找到的客户IComparer:

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\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

最后一个注释我在SortedDictionary上找到的每个资源都说明它们比Dictionary更加耗费资源,所以请注意非常大的资源。但是,我只能希望我的文件夹结构不会增长太多,所以这对我来说是可以接受的。

答案 2 :(得分:-2)

请尝试以下操作:

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\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"
                             };

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

        }
    }
}