2010-10-03 6 views
6

Ich habe eine Abfrage in LinqToSql, die eine LabelNumber zurückgibt:C# Linq Custom Sort

var q = from list in db.Lists 
     select list.LabelNumber; 

var q dann eine IEnumerable<string> mit Elementen wie dies zu:

{"1","2","2.A","2.B","3","3.A","3.B"} 

ich im Grunde die Elemente bestellen möchten als sie erscheinen oben, aber ich kann die OrderBy(x=>x.LabelNumber) nicht verwenden, weil "10" nach "1" und vor "2" platziert werden würde.

Ich nehme an, ich muss eine benutzerdefinierte Komparatorfunktion schreiben, aber wie mache ich das mit Linq?

Edit: Ich denke, alle Antworten unten funktionieren, aber ein Vorbehalt muss zu allen Antworten hinzugefügt werden.

Wenn Sie Linq2SQL verwenden, können Sie keine Array-Indizes innerhalb der Abfrage verwenden. Um dies zu vermeiden, sollten Sie zwei Abfragen haben. Eine, die aus SQL liest. Die zweite funktioniert die Bestellung:

var q = from list in db.Lists 
      select list.LabelNumber; 

var q2 = q.AsEnumerable() 
      .OrderBy(x => int.Parse(x.LabelNumber.Split('.')[0])) 
      .ThenBy(x => x.Number 
         .Contains(".") ? 
           x.LabelNumber.Split('.')[1].ToString() 
           : 
           string.Empty); 
+0

FYI, sortieren die Zahlen, wie Sie wollen, wird eine "natürliche Art" genannt. – mquander

Antwort

9

Sie haben wahrscheinlich zu keinen benutzerdefinierten comparer zu schreiben. Wenn alle Ihre Etiketten in der Form number.letter sind, könnten Sie dies verwenden.

Wenn Sie mehr Kontrolle benötigen, können Sie immer die sortby Felder konvertieren (a und b) an den entsprechenden Typen statt Ints und Streicher.

Wenn dies LINQ-zu-SQL ist, wird dies tatsächlich nicht funktionieren, da einige hier verwendete Methoden nicht unterstützt werden. Hier ist eine LINQ-to-SQL-freundliche Version. Es wird nicht die schönste Abfrage ergeben, aber es wird funktionieren.

var query = from list in db.Lists 
      let dot = list.LabelNumber.IndexOf('.') 
      let name = list.LabelNumber 
      let order = dot == -1 
       ? new { a = Convert.ToInt32(name.Substring(0, dot)), b = String.Empty } 
       : new { a = Convert.ToInt32(name.Substring(0, dot)), b = name.Substring(dot+1) } 
      orderby order.a, order.b 
      select list.LabelNumber; 
+0

Ich erhalte diesen Fehler, wenn ich dies ausführe: 'System.InvalidOperationException: Unerkannter Expression-Knoten: ArrayIndex' – Shawn

+0

Dies ist in LINQ-to-SQL richtig? Natürlich würde das dort nicht funktionieren und würde einige Feinabstimmungen erfordern. –

+0

Ich habe eine Bearbeitung hinzugefügt, die die Optimierung anspricht. Vielen Dank. – Shawn

1

Wenn Sie sicher sind, dass q ist gut formatiert und sequenziert:

var result = q.OrderBy(x => int.Parse(x.Split('.')[0])); 
+2

Sie gehen davon aus, dass nur die Nummern außer Betrieb sind. Was wäre, wenn du 2.B, 2.2.A hättest? –

+0

@Michael: Der OP hat nicht gesagt, dass er durch die Buchstaben bestellen will..und ich bin faul genug. Jedenfalls ist es ziemlich wahrscheinlich, dass er es will. LOL. –

11
OrderBy(x=>x.LabelNumber, new AlphanumComparator()) 

wo AlphanumComparator die ausgezeichnete Alphanum natural sort algorithm von David Koelle ist. Keine Notwendigkeit, das Rad neu zu erfinden.

Wenn du wirst die Version C# ändern Sie es zu:

AlphanumComparator : IComparer<string> 

und

public int Compare(string x, string y) 
+3

+1, für die Einführung in AlphanumComparator –

+0

Diese Syntax funktioniert nicht. 'Argument 3: Konvertierung von 'AlphanumComparator' in 'System.Collections.Generic.IComparer ' ' – Shawn

+0

nicht möglich, da OrderBy einen generischen IComparer erwartet, während AlphanumComparator nicht generisch ist. Die Änderung ist jedoch ziemlich einfach. Fügen Sie die in den Deklarationen hinzu, ändern Sie alle Objekte in Ts und entfernen Sie die Umwandlungen. – Kyte

0

Hier ist mein Beitrag .. mit Regular Expression und LAMBDA expression

List<String> Lst = new List<string> { "1", "2", "2.A","10.A", "2.C", "3", "3.A", "3.B","2.B","11.D" }; 
Lst = Lst.Select(X => new 
     { 
      Number = int.Parse(Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[1].Value), 
      Strings=Regex.Match(X, @"([0-9]*).?([a-zA-Z]*)").Groups[2].Value, 
      OriginalString = X 
     }).OrderBy(X => X.Number).ThenBy(X=>X.Strings) 
     .Select(X => X.OriginalString).ToList(); 

Ausgang :

"1" 
"2" 
"2.A" 
"2.B" 
"2.C" 
"3" 
"3.A" 
"3.B" 
"10.A" 
"11.D" 
+0

'Methode 'System.Text.RegularExpressions.Match (System.String, System.String)' hat keine unterstützte Übersetzung in SQL.Obwohl es scheint, in Ihrem Beispiel zu funktionieren. – Shawn

+0

Dann erstellen Sie eine "Liste" mit Ihren Label-Nummern und sortieren Sie diese dann mit dem obigen Ausdruck. –

+0

Habe es gelöst. Danke, ich habe Code, der mit Linq2SQL läuft. Ich war mir der Array-Beschränkung nicht bewusst. Ich habe am Ende eine einfachere Version verwendet, da Regex meine Situation komplizierter macht. Die Sub-Nesting wird schließlich komplizierter und ich muss auf Ihre Lösung zurückgreifen. Vielen Dank. – Shawn