Как заказать этот список URL-адресов сайтов в C#?

у меня есть список URL-адресов сайта

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

список дается мне в случайном порядке, мне нужно заказать его так, чтобы верхний уровень был первым, а затем подуровни и так далее (потому что я не могу создать /node2/sub-node1 без /node2 существующие). Есть чистый способ сделать это?

прямо сейчас я просто делаю рекурсивный вызов, говоря, что если я не могу создать sub-node1, потому что создать node2. Я хотел бы, чтобы порядок списка определял создание и избавлялся от моего рекурсивного вызова.

7 ответов


моей первой мыслью было упорядочить по длине строки... но потом я подумал о таком списке, который может включать что-то вроде псевдонимов для коротких имен:

/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 / sub-node1 без /node2 существующий

Аха! Порядок разделов или внутри раздела на самом деле не имеет значения, если дети всегда перечислены после родителей. Имея это в виду, моя первоначальная мысль была в порядке, и порядок по длине строки должен быть просто прекрасным:

IEnumerable<string> SortURLs(IEnumerable<string> urls)
{
    return urls.OrderBy(s => s.Length);
}

что привело меня, наконец, к удивлению, почему я вообще заботился о длине? Если я просто сортирую строки, независимо от длины, строки с тем же началом всегда сначала сортируют более короткую строку. Так, наконец:--5-->

IEnumerable<string> SortURLs(IEnumerable<string> urls)
{
    return urls.OrderBy(s => s);
}

Я оставлю первый образец, потому что это может быть полезно, если в какой-то момент в будущем вам понадобится более лексический или логический порядок сортировки.


есть ли чистый способ сделать это?

просто сортировка списка URI с использованием стандартной сортировки строк должна дать вам то, что вам нужно. В общем, "а" того, до "АА" в строке "сортировка", так "/узел1" должны закончиться до "/узел1/суб-узел".

например:

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

возможно, это работает для вас:

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

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

, который не отсортирован.

Вы можете посмотреть это реализация


если вы имеете в виду, что вам нужны все узлы первого уровня перед всеми узлами второго уровня, отсортируйте по количеству косых черт /:

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

рекурсия на самом деле именно то, что вы должны использовать, так как это наиболее легко представлено древовидной структурой.

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