2012-01-23 7 views
8

Mam różne rozdziały o różnych głębokościach.Sortowanie rozdziałów takich jak 14.1.2.3 i 14.10.1.2.3.4

więc są 14.1 i 14.4.2 i 14.7.8.8.2 i tak dalej.

Alfanumeryczne posortowane 14.10 pojawi się przed 14.2. To źle. Powinno przyjść po 14.9.

Czy istnieje prosty sposób sortowania theese bez dodawania zer wiodących? f.e. z linq?

+2

Myślę, że zaakceptowana odpowiedź [tutaj] (http://stackoverflow.com/questions/6248039/how-to-sort-list-of-ip-addresses-using-c- sharp) jest również ważna dla twojej sprawy. chociaż będziesz ograniczony do czterech "poziomów". –

+0

wygląda całkiem ładnie, 4 poziomy - ok. Pomysły na jeszcze więcej byłoby fajnie :) – Harry

Odpowiedz

7
public class NumberedSectionComparer : IComparer<string> 
{ 
    private int Compare(string[] x, string[]y) 
    { 
    if(x.Length > y.Length) 
     return -Compare(y, x);//saves needing separate logic. 
    for(int i = 0; i != x.Length; ++i) 
    { 
     int cmp = int.Parse(x[i]).CompareTo(int.Parse(y[i])); 
     if(cmp != 0) 
     return cmp; 
    } 
    return x.Length == y.Length ? 0 : -1; 
    } 
    public int Compare(string x, string y) 
    { 
    if(ReferenceEquals(x, y))//short-cut 
     return 0; 
    if(x == null) 
     return -1; 
    if(y == null) 
     return 1; 
    try 
    { 
     return Compare(x.Split('.'), y.Split('.')); 
    } 
    catch(FormatException) 
    { 
     throw new ArgumentException(); 
    } 
    } 
} 
+0

To działa! Podoba mi się styl, w którym to rozwiązałeś. Dzięki. – Harry

4

Zrobiłem to teraz, trzeba kilka testów:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace TestesConsole 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string[] vers = new[] 
           { 
            "14.10", 
            "14.9", 
            "14.10.1", 
           }; 


      var ordered = vers.OrderBy(x => x, new VersionComparer()).ToList(); 

     } 
    } 

    public class VersionComparer : IComparer<string> 
    { 
     public int Compare(string x, string y) 
     { 
      string[] xs = x.Split('.'); 
      string[] ys = y.Split('.'); 

      int maxLoop = Math.Min(xs.Length, ys.Length); 

      for (int i = 0; i < maxLoop; i++) 
      { 
       if(int.Parse(xs[i]) > int.Parse(ys[i])) 
       { 
        return 1; 
       } 
       else if(int.Parse(xs[i]) < int.Parse(ys[i])) 
       { 
        return -1; 
       } 
      } 

      if(xs.Length > ys.Length) 
      { 
       return 1; 
      } 
      else if(xs.Length < ys.Length) 
      { 
       return -1; 
      } 

      return 0; 
     } 
    } 
} 
+0

Najpierw przetestowałem to rozwiązanie, a z jakiegoś powodu jego działanie w ogromnym przetwarzaniu kończącym się maksymalnym czasem wykonania ... Dziękuję bardzo za poświęcony czas, ale dla mnie, z jakiegoś tajemniczego powodu nie działa bardzo dobrze. : | – Harry

+0

Nie ładowałem testów, tylko dla ciekawości, ile masz w swojej kolekcji? –

+0

Istnieje około 1500 różnych przedmiotów, 1700 lub coś w sumie :) @fujiy – Harry

1
var headers = new List<string> {"14.1.2.3", "14.1", "14.9", "14.2.1", "14.4.2", "14.10.1.2.3.4", "14.7.8.8.2"}; 
    headers.Sort(new MySorter()); 



class MySorter : IComparer<string> 
    { 
    public int Compare(string x, string y) 
    { 
    IList<string> a = x.Split('.'); 
    IList<string> b = y.Split('.'); 
    int numToCompare = (a.Count < b.Count) ? a.Count : b.Count; 
    for (int i = 0; i < numToCompare; i++) 
    { 
    if (a[i].Equals(b[i])) 
    continue; 
    int numa = Convert.ToInt32(a[i]); 
    int numb = Convert.ToInt32(b[i]); 
    return numa.CompareTo(numb); 
    } 
    return a.Count.CompareTo(b.Count); 
    } 

    } 
1

Korzystanie IComparer hast wielką wadę, powtarzając raczej drogie obliczenia bardzo często, więc pomyślałem precalculating się criterium zamówienia będzie być dobrym pomysłem:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ChapterSort 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      String[] chapters=new String[] {"14.1","14.4.2","14.7.8.8.2","14.10","14.2","14.9","14.10.1.2.3.4","14.1.2.3" }; 

      IEnumerable<String> newchapters=chapters.OrderBy(x => new ChapterNumerizer(x,256,8).NumericValue); 

      foreach (String s in newchapters) Console.WriteLine(s); 

     } 
    } 

    public class ChapterNumerizer 
    { 
     private long numval; 

     public long NumericValue {get{return numval;}} 

     public ChapterNumerizer (string chapter,int n, int m) 
     { 
      string[] c = chapter.Split('.'); 
      numval=0; 
      int j=0; 

      foreach (String cc in c) 
      { 
       numval=n*numval+int.Parse(cc); 
       j++; 
      } 
      while (j<m) 
      { 
       numval*=n; 
       j++; 
      } 
     } 
    } 
} 
1

To rozwiązanie jest bardziej ogólne.

public class SequenceComparer<T> : IComparer<IEnumerable<T>> where T : IComparable<T> 
{ 
    public int Compare(IEnumerable<T> x, IEnumerable<T> y) 
    { 
     IEnumerator<T> enx = x.GetEnumerator(); 
     IEnumerator<T> eny = y.GetEnumerator(); 

     do 
     { 
      bool endx = enx.MoveNext(); 
      bool endy = eny.MoveNext(); 

      if (!endx && !endy) 
       return 0; 

      if (!endx) 
       return -1; 

      if (!endy) 
       return 1; 

      var comp = enx.Current.CompareTo(eny.Current); 
      if(comp != 0) 
       return comp; 
     } while (true); 
    } 
} 

Następnie użyj:

var sv = vers.Select(v => new { Key = v, Split = v.Split('.').Select(Int32.Parse) }); 
var ordered = sv.OrderBy(x => x.Split, new SequenceComparer<int>()).Select(x => x.Key); 
1

Jako mały LINQ jedno-liner:

List<string> chapters= new List<string>() 
{ 
    "14.1", 
    "14.4.2", 
    "14.7.8.8.2", 
    "14.10", 
    "14.2" 
}; 

chapters.OrderBy(c => Regex.Replace(c, "[0-9]+", match => match.Value.PadLeft(10, '0'))); 

Niezależny od poziomu, ale na pewno nie najlepsze wykonanie ...

Kredyty są przechodząc na https://stackoverflow.com/a/5093939/226278

Powiązane problemy