我有一个站点URL列表:
/node1
/node1/sub-node1
/node2
/node2/sub-node1
该列表以随机顺序给出,我需要对其进行排序,使得顶级节点首先出现,然后是子级节点等等(因为我不能创建/node2/sub-node1
而没有/node2
的存在)。有没有一种简洁的方法来做到这一点?
现在我只是进行递归调用,如果我不能创建sub-node1
,因为node2
已经存在,则创建node2
。我想让列表的顺序决定创建顺序,并摆脱我的递归调用。
我有一个站点URL列表:
/node1
/node1/sub-node1
/node2
/node2/sub-node1
该列表以随机顺序给出,我需要对其进行排序,使得顶级节点首先出现,然后是子级节点等等(因为我不能创建/node2/sub-node1
而没有/node2
的存在)。有没有一种简洁的方法来做到这一点?
现在我只是进行递归调用,如果我不能创建sub-node1
,因为node2
已经存在,则创建node2
。我想让列表的顺序决定创建顺序,并摆脱我的递归调用。
我的第一个想法是按字符串长度排序...但后来我想到了这样的列表,它可能包括一些短名称的别名:
/longsitename/ /a /a/b/c/ /a /a/b/ /otherlongsitename/
...然后我认为更好的选择是首先按层级分隔符字符的数量进行排序:
IEnumerable<string> SortURLs(IEnumerable<string> urls)
{
return urls.OrderBy(s => s.Count(c => c == '/')).ThenBy(s => s);
}
我再仔细想了想,看到你问题中的这一行:
如果没有 /node2 ,我就无法创建 /node2/sub-node1
哦!节点的顺序或是同一节点内部的顺序并不重要,只要子节点总是在父节点之后。有了这个想法,我原来的想法就可以了,按照字符串长度排序就可以了:
IEnumerable<string> SortURLs(IEnumerable<string> urls)
{
return urls.OrderBy(s => s.Length);
}
最终使我想知道为什么我关心字符串的长度?如果我只是按顺序排列这些字符串,而不考虑它们的长度,那么具有相同开头的字符串将始终首先排序短字符串。因此,最终:
IEnumerable<string> SortURLs(IEnumerable<string> urls)
{
return urls.OrderBy(s => s);
}
我会保留第一个示例,因为如果将来需要更加词汇或逻辑排序的话,它可能会有用。
有没有一种简洁的方法来做到这一点?
只需使用标准字符串排序对URI列表进行排序即可获得所需结果。通常,在字符串排序中,“a”将在“aa”之前排序,因此“/node1”应该排在“/node1/sub-node”之前。
例如:
List<string> test = new List<string> { "/node1/sub-node1", "/node2/sub-node1", "/node1", "/node2" };
foreach(var uri in test.OrderBy(s => s))
Console.WriteLine(uri);
/node1
/node1/sub-node1
/node2
/node2/sub-node1
/node10
呢? - It'sNotALie./node10
在/node10/somethingelse
之前的情况,因为这是用于文件夹构建的。 - Reed Copsey/node10
在/node10/sub-node1
之前,从他所做的评论中可以看出,如果单个目录层次结构内的项目逻辑有序,那么/node10
在/node1
之前并不重要。根项的顺序是无关紧要的。 - Scott Chamberlainvar nodes = new[] { "/node1", "/node1/sub-node1", "/node2", "/node2/sub-node1" };
var orderedNodes = nodes
.Select(n => new { Levels = Path.GetFullPath(n).Split('\\').Length, Node = n })
.OrderBy(p => p.Levels).ThenBy(p => p.Node);
结果:
foreach(var nodeInfo in orderedNodes)
{
Console.WriteLine("Path:{0} Depth:{1}", nodeInfo.Node, nodeInfo.Levels);
}
Path:/node1 Depth:2
Path:/node2 Depth:2
Path:/node1/sub-node1 Depth:3
Path:/node2/sub-node1 Depth:3
/
而不是 '\\'
。 - It'sNotALie.如果您需要在所有第二级节点之前获取所有第一级节点,那么请按斜杠/
的数量进行排序:
string[] array = {"/node1","/node1/sub-node1", "/node2", "/node2/sub-node1"};
array = array.OrderBy(s => s.Count(c => c == '/')).ToArray();
foreach(string s in array)
System.Console.WriteLine(s);
结果:
/node1
/node2
/node1/sub-node1
/node2/sub-node1
Array.Sort(array);
结果:
/node1
/node1/sub-node1
/node2
/node2/sub-node1
var values = new string[]{"/node1", "/node1/sub-node1" ,"/node2", "/node2/sub-node1"};
foreach(var val in values.OrderBy(e => e))
{
Console.WriteLine(val);
}
List<string> test = new List<string> { "/node1/sub-node1" ,"/node13","/node10","/node2/sub-node1", "/node1", "/node2" };
输出结果将是:
/node1
/node1/sub-node1
/node10
/node13
/node2
/node2/sub-node1
这个没有排序。
你可以查看这个实现
递归实际上是你应该使用的,因为这最容易用树形结构表示。
public class PathNode {
public readonly string Name;
private readonly IDictionary<string, PathNode> _children;
public PathNode(string name) {
Name = name;
_children = new Dictionary<string, PathNode>(StringComparer.InvariantCultureIgnoreCase);
}
public PathNode AddChild(string name) {
PathNode child;
if (_children.TryGetValue(name, out child)) {
return child;
}
child = new PathNode(name);
_children.Add(name, child);
return child;
}
public void Traverse(Action<PathNode> action) {
action(this);
foreach (var pathNode in _children.OrderBy(kvp => kvp.Key)) {
pathNode.Value.Traverse(action);
}
}
}
之后你可以像这样使用:
var root = new PathNode(String.Empty);
var links = new[] { "/node1/sub-node1", "/node1", "/node2/sub-node-2", "/node2", "/node2/sub-node-1" };
foreach (var link in links) {
if (String.IsNullOrWhiteSpace(link)) {
continue;
}
var node = root;
var lastIndex = link.IndexOf("/", StringComparison.InvariantCultureIgnoreCase);
if (lastIndex < 0) {
node.AddChild(link);
continue;
}
while (lastIndex >= 0) {
lastIndex = link.IndexOf("/", lastIndex + 1, StringComparison.InvariantCultureIgnoreCase);
node = node.AddChild(lastIndex > 0
? link.Substring(0, lastIndex) // Still inside the link
: link // No more slashies
);
}
}
var orderedLinks = new List<string>();
root.Traverse(pn => orderedLinks.Add(pn.Name));
foreach (var orderedLink in orderedLinks.Where(l => !String.IsNullOrWhiteSpace(l))) {
Console.Out.WriteLine(orderedLink);
}
应该打印:
/node1
/node1/sub-node1
/node2
/node2/sub-node-1
/node2/sub-node-2