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;
}
}
现在你可以在SortedDictionary、HashSet、SortedList等中使用它,也作为
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
资源密集得多,所以要小心非常大的资源那些。但是,我只能希望我的文件夹结构不会增长太多,所以这对我来说是可以接受的。
我有一个程序,我按文件目录的顺序解析 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;
}
}
现在你可以在SortedDictionary、HashSet、SortedList等中使用它,也作为
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
资源密集得多,所以要小心非常大的资源那些。但是,我只能希望我的文件夹结构不会增长太多,所以这对我来说是可以接受的。