9

我有一个网站 URL 列表,

  • /node1
  • /node1/sub-node1
  • /node2
  • /node2/sub-node1

该列表以随机顺序提供给我,我需要对其进行排序,因此顶级是第一,然后是子级别,依此类推(因为我无法在/node2/sub-node1没有/node2存在的情况下创建)。有没有一种干净的方法可以做到这一点?

现在我只是在进行递归调用,说如果我sub-node1因为node2存在而无法创建,请创建node2。我想让列表的顺序确定创建并摆脱我的递归调用。

4

7 回答 7

6

我的第一个想法是按字符串的长度排序......但后来我想到了一个这样的列表,其中可能包括类似短名称的别名:

/长网站名称/  
/一种  
/a/b/c/  
/一种  
/a/b/  
/其他长站点名称/  

...我认为更好的选择是首先按级别分隔符的数量排序:

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

我将保留第一个示例,因为如果在将来的某个时候,您需要更词汇或逻辑的排序顺序,它可能会很有用。

于 2013-06-14T16:13:20.530 回答
2

有没有一种干净的方法可以做到这一点?

只需使用标准字符串排序对 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
于 2013-06-14T16:09:24.830 回答
2

也许这对你有用:

var 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
于 2013-06-14T16:14:26.200 回答
0

如果您的意思是在所有第二级节点之前需要所有第一级节点,请按斜杠数排序/

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
于 2013-06-14T16:11:30.273 回答
0
var values = new string[]{"/node1", "/node1/sub-node1" ,"/node2", "/node2/sub-node1"};
foreach(var val in values.OrderBy(e => e))
{
    Console.WriteLine(val);
}
于 2013-06-14T16:15:07.490 回答
0

最好是使用自然排序,因为您的字符串混合在字符串和数字之间。因为如果您使用其他排序方法或技术并且您喜欢这个示例:

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

未排序。

你可以看看这个实现

于 2013-06-14T16:44:43.577 回答
0

递归实际上正是您应该使用的,因为它最容易用树结构表示。

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
于 2013-06-14T18:15:34.120 回答