2016-02-12 14 views
5

Ich bin relativ neu zu LINQ und arbeitet derzeit an einer Abfrage, die Gruppierung und Sortierung kombiniert. Ich werde hier mit einem Beispiel beginnen. Im Prinzip habe ich eine beliebige Folge von Zahlen als Zeichenfolge dargestellt:LINQ-Abfrage, die Gruppierung und Sortierung kombiniert

List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"} 

Ich brauche all sNumbers in dieser Liste zu finden, die ein Suchmuster enthalten (etwa „384“) dann die gefilterte Sequenz zurückkehren, so dass die sNumbers, dass Beginnen Sie mit dem Suchmuster ("384") werden zuerst sortiert, gefolgt von den restlichen sNumbers, die das Suchmuster irgendwo enthalten. So wird es wie folgt aus (bitte auch in den Gruppen die alphabetische Sortierung bemerken):

{"38450", "38451", "13841", "28384", "138477"} 

Hier ist, wie ich begonnen habe:

outputlist = (from n in sNumbers 
       where n.Contains(searchPattern 
       select n).ToList(); 

So, jetzt haben wir alle Zahl, die die Suche enthalten Muster. Und hier stecke ich fest. Ich weiß, dass ich an dieser Stelle die Ergebnisse in zwei Sequenzen "gruppieren" muss. Eine, die mit dem Suchmuster beginnt und andere nicht. Wenden Sie dann eine sekundäre Sortierung in jeder Gruppe alphabetisch an. Wie schreibe ich eine Abfrage, die all das kombiniert?

+0

Sorry, nach dem Lesen durch die Antworten, ich merke, dass ich einen Fehler oben gemacht die richtige Antwort wäre. wie folgt: (Sortieren nach beginnt mit und dann alphabetisch (nicht numerisch) {"38450", "38451", "13841", "138477", "28384"} –

Antwort

2

Diese recht geradlinig scheint, wenn ich etwas falsch verstanden haben:

List<string> outputlist = 
    sNumbers 
     .Where(n => n.Contains("384")) 
     .OrderBy(n => int.Parse(n)) 
     .OrderByDescending(n => n.StartsWith("384")) 
     .ToList(); 

bekomme ich diese:

outputlist

+0

Einzige Sache ist, dass die sekundäre Art alphabetisch war, nicht numerisch. Ich konnte das erreichen, indem ich folgenden Code schrieb: Liste Ergebnis = sNumbers . Wo (n => n.Enthält (Suchmuster)) .OrderBy (s =>! S.StartsWith (Suchmuster)) .ThenBy (s => s) .ToList(); Danke für Ihre Hilfe –

2

Hier ist die optimierte Version, die nur eine LINQ-Anweisung benötigt:

string match = "384"; 
List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"}; 

// That's all it is 
var result = 
    (from x in sNumbers 
    group x by new { Start = x.StartsWith(match), Contain = x.Contains(match)} 
    into g 
    where g.Key.Start || g.Key.Contain 
    orderby !g.Key.Start 
    select g.OrderBy(Convert.ToInt32)).SelectMany(x => x); 

result.ToList().ForEach(x => Console.Write(x + " ")); 

Schritte:

1.) Gruppe in Gruppe g basierend auf Starts und enthält

2.) Wählen Sie einfach die Gruppen aus, die die Übereinstimmung enthalten

3.) Sortieren nach dem Kehrwert des Start Taste (Damit Starts = true kommen vor Start = false)

4.)

5. die sortierte Liste der Elemente beiden Gruppen auswählen) Führen Sie eine flatMap (Select) über beide Listen eine endgültige Ergebnisliste


Hier eine nicht optimierte Version zu erhalten:

string match = "384"; 
List<string> sNumbers = new List<string> {"34521", "38450", "138477", "38451", "28384", "13841", "12345"}; 
var matching = from x in sNumbers 
       where x.StartsWith(match) 
       orderby Convert.ToInt32(x) 
       select x; 
var nonMatching = from x in sNumbers 
        where !x.StartsWith(match) && x.Contains(match) 
        orderby Convert.ToInt32(x) 
        select x; 
var result = matching.Concat(nonMatching); 

result.ToList().ForEach(x => Console.Write(x + " ")); 
+1

Danke für Ihre Hilfe. Ich konnte es mit einer einfacheren Lösung unter Verwendung der Syntax der Erweiterungsmethode erreichen. Fike –

0

Linq hat eine OrderBy-Methode, die Sie für Dezember geben eine benutzerdefinierte Klasse erlaubt wie Dinge sortiert werden sollen. Schau mal hier: https://msdn.microsoft.com/en-us/library/bb549422(v=vs.100).aspx

Dann kannst du deine IComparer-Klasse schreiben, die einen Wert im Konstruktor nimmt, dann eine Compare-Methode, die Werte vorzieht, die mit diesem Wert beginnen.

So etwas wie dies vielleicht:

public class CompareStringsWithPreference : IComparer<string> { 
    private _valueToPrefer; 

    public CompareStringsWithPreference(string valueToPrefer) { 
     _valueToPrefer = valueToPrefer; 
    } 

    public int Compare(string s1, string s2) { 
     if ((s1.StartsWith(_valueToPrefer) && s2.StartsWith(_valueToPrefer)) || 
      (!s1.StartsWith(_valueToPrefer) && !s2.StartsWith(_valueToPrefer))) 
       return string.Compare(s1, s2, true); 

     if (s1.StartsWith(_valueToPrefer)) return -1; 
     if (s2.StartsWith(_valueToPrefer)) return 1; 
    } 
} 

es dann wie folgt verwenden:

outputlist = (from n in sNumbers 
      where n.Contains(searchPattern) 
      select n).OrderBy(n, new CompareStringsWithPreference(searchPattern))ToList(); 
+0

Sie müssen n.Contains (searchPattern) entfernen. Wenn Sie das nicht tun, werden Sie nur mit einer Teilmenge arbeiten. – CodeNotFound

+0

Von dem, was ich verstanden habe, will er: "Ich muss alle sNumbers in dieser Liste finden, die ein Suchmuster enthalten (zB" 384 ") und dann die gefilterte Sequenz zurückgeben dass die sNumbers, die mit dem Suchmuster ("384") beginnen, zuerst sortiert werden. " –

0

Sie eine Liste mit Strings, beginnend mit searchPattern Variable erstellen und andere enthalten searchPattern aber mit nicht beginnen (zu vermeiden Sie, Elemente in beiden Listen zu wiederholen):

string searchPattern = "384"; 
List<string> sNumbers = new List<string> { "34521", "38450", "138477", "38451", "28384", "13841", "12345" }; 

var list1 = sNumbers.Where(s => s.StartsWith(searchPattern)).OrderBy(s => s).ToList(); 
var list2 = sNumbers.Where(s => !s.StartsWith(searchPattern) && s.Contains(searchPattern)).OrderBy(s => s).ToList(); 

var outputList = new List<string>(); 

outputList.AddRange(list1); 
outputList.AddRange(list2); 
2
var result = sNumbers 
         .Where(e => e.StartsWith("384")) 
         .OrderBy(e => Int32.Parse(e)) 
       .Union(sNumbers 
         .Where(e => e.Contains("384")) 
         .OrderBy(e => Int32.Parse(e))); 
+0

Dies wird nicht numerisch sortiert –

+1

@MarkusWeninger, ja, Sie haben Recht, behoben, danke –

+0

Ihre Antwort macht Sinn, aber wir sortieren alphabetisch, nicht numerisch –

3

Ich glaube, Sie brauchen keine Gruppierung noch Liste Splitting Ihr gewünschtes Ergebnis zu erzielen, so dass anstelle der Antwort über die Kombination und die Gruppierung Ich werde schreiben, was ich tun würde, gewünschtes Ergebnis zu erhalten:

sNumbers.Where(x=>x.Contains(pattern)) 
    .OrderByDescending(x => x.StartsWith(pattern)) // first criteria 
    .ThenBy(x=>Convert.ToInt32(x)) //this do the trick instead of GroupBy 
    .ToList(); 
0

Sorry Leute, nachdem ich die Antworten gelesen habe, merke ich, dass ich einen Fehler in meiner Frage gemacht habe. Die richtige Antwort wäre wie folgt: (Sortieren nach "beginnt mit" zuerst und dann alphabetisch (nicht numerisch)

// Ausgabe: {"38450", "38451", "13841", "138477", "28384 „}

konnte ich das mit der folgenden Abfrage erreichen:

string searchPattern = "384"; 
List<string> result = 
          sNumbers 
           .Where(n => n.Contains(searchpattern)) 
           .OrderBy(s => !s.StartsWith(searchpattern)) 
           .ThenBy(s => s) 
           .ToList(); 

Dank

Verwandte Themen