2016-02-12 66 views
5

我對LINQ相對較新,目前正在處理將分組和排序相結合的查詢。我將從這裏開始一個例子。基本上我有字符串表示的數字的任意序列:結合分組和排序的LINQ查詢

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

我需要找到所有sNumbers在這個列表中包含的搜索模式(比如「384」) 然後返回過濾序列使得sNumbers那首先搜索模式(「384」)首先排序,然後是其餘sNumbers,其中包含某處的搜索模式。因此,這將是這樣的(請注意,在該組中的字母排序):

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

這是我如何開始:

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

所以,現在我們已經包含搜索所有號碼模式。這就是我被卡住的地方。我知道在這一點上我需要將結果分成兩個序列。一個以搜索模式開始,另一個則不然。然後按字母順序在每個組中應用次要排序。我如何編寫一個結合了所有這些的查詢?

+0

對不起,通過響應看完之後,我意識到我犯了上述錯誤的正確答案應該是。如下所示:(按開頭和按字母順序排序(不是數字) {「38450」,「38451」,「13841」,「138477」,「28384」} –

回答

2

這似乎相當直截了當,除非我誤解的東西:

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

我得到這個:

outputlist

+0

唯一的事情是次要排序是按字母順序排列,而不是數字。我能夠通過以下代碼實現: 列表結果= sNumbers 。其中(n => n。(s => s) .ToList();包含 感謝您的幫助 –

2

這裏優化版本其中只需要一個LINQ語句:

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

步驟:

1)組成基於StartsWith組和包含

2。 )只需選擇那些包含匹配的組

3)由StartsWith按鍵相反的順序(這樣StartsWith =真正到來之前StartsWith = FALSE)

4)選擇兩組

5)做一個flatMap(的SelectMany)以上的元素的排序列表這兩個列表接收一個最終結果列表


這裏未優化的版本:

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

Thx爲您提供幫助。我能夠通過使用擴展方法語法的更簡單的解決方案來實現它。 Fike –

0

的LINQ有一個排序依據的方法,它允許您爲十二月的自定義類確定如何分類。看這裏:https://msdn.microsoft.com/en-us/library/bb549422(v=vs.100).aspx

然後,你可以編寫你的IComparer類,它在構造函數中接受一個值,然後是一個比較方法,它喜歡以該值開始的值。

事情是這樣的,也許:

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

然後使用它是這樣的:

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

您必須刪除n.Contains(searchPattern)。如果不這樣做,你會只與一個子集 – CodeNotFound

+0

工作,從我的理解,這就是他想要的東西:「我需要找到在這個列表中包含搜索模式的所有sNumbers(說‘384’),然後返回過濾序列,以搜索模式(「384」)開始的sNumber首先被排序。「 –

0

您可以創建的字符串開始searchPattern變量,另一種含searchPattern但與首發名單(以避免在兩個列表中重複元素):

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

這不會排序數字 –

+1

@ MarkusWeninger,是的,你是對的,修復,謝謝 –

+0

你的答案是有道理的,但我們按字母順序排序,而不是數字 –

3

我覺得你不需要讓你的期望結果的任何分組名單也沒有分裂,所以不是關於合併和分組我會後我會做什麼,從而獲得所需結果答案:

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

對不起球員,通過響應看完之後,我意識到,我在我的問題犯了一個錯誤。正確的答案如下:(按「開頭」,然後按字母順序排序(不是數字)

//輸出:{「38450」,「38451」,「13841」,「138477」,「28384 「}

我能做到這一點與下面的查詢:

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

感謝