2013-06-14 13 views
8

Mam listę adresów URL witryny,Jak zamówić tę listę adresów URL witryny w języku C#?

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

Lista jest mi dane w kolejności losowej, muszę zamówić tak więc najwyższy poziom jest pierwszy, a następnie poziomy podrzędne i tak dalej (ponieważ nie mogę utworzyć /node2/sub-node1 istniejący). Czy jest to czysty sposób na zrobienie tego?

Właśnie wykonuję wywołanie rekursywne, mówiąc, że jeśli nie mogę utworzyć sub-node1, ponieważ istnieje node2, utwórz node2. Chciałbym, aby kolejność listy determinowała tworzenie i pozbawianie się rekurencyjnego wywołania.

+2

To dalszy rodzaj pomocy można umieścić w dół, co już próbowałem. –

+0

Co zrobić, jeśli najpierw odwrócisz każdy ciąg przed sortowaniem? – cgTag

+0

Co masz na myśli przez 'create node2'? Być może istnieje implementacja, która nie będzie potrzebować nawet poziomów root na liście ... – BrunoLM

Odpowiedz

5

Moja pierwsza myśl była zamówieniu przez długości łańcucha ... ale potem pomyślałem o takiej liście, która może zawierać coś w rodzaju aliasów dla krótkich nazw:

 
/longsitename/ 
/a 
/a/b/c/ 
/a 
/a/b/ 
/otherlongsitename/

... i pomyślałem, lepszym rozwiązaniem było zamówić przez liczbę znaków Separator poziom pierwszy:

IEnumerable<string> SortURLs(IEnumerable<string> urls) 
{ 
    return urls.OrderBy(s => s.Count(c => c == '/')).ThenBy(s => s); 
} 

Wtedy pomyślałem o nim trochę więcej i widziałem ten wiersz w swoim pytaniu:

nie mogę utworzyć/nODE2/sub-node1 bez/node2 istniejący

Aha!Kolejność sekcji lub sekcji nie ma znaczenia, o ile dzieci są zawsze wymienione po rodzicach. Mając to na uwadze, moja oryginalna myśl była w porządku i zamawianie przez długości łańcucha sam powinien być dobrze:

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

Które mnie doprowadzić w końcu do zastanawiać, dlaczego ja dbał o długości w ogóle? Jeśli po prostu posortuję ciągi, niezależnie od długości, ciągi o tym samym początku będą zawsze sortować najpierw krótszy ciąg. Tak więc, w końcu:

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

Zostawię pierwszą próbkę, bo może to być przydatne, jeśli w pewnym momencie w przyszłości, trzeba bardziej leksykalny lub logiczny porządek.

+0

To nie zadziała ... '"/"' nie jest 'char' ... –

+0

@newStackExchangeInstance Oops. Naprawiono teraz. –

+0

+1 sam pomysł, ale najpierw wyprodukowałeś dokładnie tę samą próbkę kodu. – jerry

2

Czy jest to czysty sposób na zrobienie tego?

Wystarczy posortować listę identyfikatorów URI przy użyciu standardowego sortowania ciągów znaków, aby uzyskać to, czego potrzebujesz. Ogólnie rzecz biorąc, "a" zamawia przed "aa" w sortowaniu ciągów, więc "/ node1" powinno kończyć się przed "/ node1/sub-node".

Na przykład:

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

ten wypisze:

/node1 
/node1/sub-node1 
/node2 
/node2/sub-node1 
+0

Dobrze, a co jeśli jest '/ node10'? –

+1

@newStackExchangeInstance Byłoby dobrze, ponieważ interesuje go tylko przypadek '/ node10' nadchodzący przed'/node10/somethingelse', ponieważ jest przeznaczony do budowy folderów. –

+0

To wciąż miałoby nielogiczne porządkowanie. –

0

Jeśli znaczy trzeba wszystkie pierwsze węzły poziomu przed wszystkimi węzłami drugiego poziomu, sort przez liczbę ukośniki /:

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

Wynik:

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

Jeśli wystarczy węzłów macierzystych przed węzłów potomnych, to nie ma się o wiele prostsze niż

Array.Sort(array); 

Wynik:

/node1 
/node1/sub-node1 
/node2 
/node2/sub-node1 
+0

Interesujący pomysł ... –

2

Może to działa dla Ciebie:

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

Wynik:

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 
+0

Nie tego szuka ... potrzebuje wszystkich podwęzłów węzła przed następnym węzłem. Również jego znak separatora to '/' not' '\\' '. –

+0

@newStackExchangeInstance Nie _eduje_ wszystkich podwęzłów jednego węzła przed następnym węzłem. Wszystkie _needs_ jest dla podwęzłów, które mają być wymienione po ich rodzicach. Oprócz tej jednej zasady wszystko może być pomieszane. –

+0

Nadal nie chciałbym tego użyć. .Split() jest powolny. –

0
var values = new string[]{"/node1", "/node1/sub-node1" ,"/node2", "/node2/sub-node1"}; 
foreach(var val in values.OrderBy(e => e)) 
{ 
    Console.WriteLine(val); 
} 
+0

Ktoś już to opublikował. –

+0

Wygląda na to, że pisałem w tym samym czasie co ktoś inny. – Casey

0

Najlepiej jest użyć naturalnego sortowania ponieważ struny są mieszane między strunami i cyfr. Bo jeśli stosować inne metody sortowania lub technik i masz jak w poniższym przykładzie:

List<string> test = new List<string> { "/node1/sub-node1" ,"/node13","/node10","/node2/sub-node1", "/node1", "/node2" }; 

wyjście będzie:

/node1 
/node1/sub-node1 
/node10 
/node13 
/node2 
/node2/sub-node1 

które nie są sortowane.

Można spojrzeć na ten Implementation

0

rekursji jest faktycznie dokładnie, co należy użyć, ponieważ najłatwiej jest reprezentowana przez strukturę drzewa.

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

które można następnie wykorzystać tak:

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

Który powinien wydrukować:

/node1 
/node1/sub-node1 
/node2 
/node2/sub-node-1 
/node2/sub-node-2 
Powiązane problemy