结合分组和排序的LINQ查询

时间:2016-02-12 22:44:02

标签: c# linq list sorting

我对LINQ相对较新,目前正致力于一项结合分组和排序的查询。我将从这里开始一个例子。基本上我有一个任意的数字序列表示为字符串:

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

我需要在此列表中找到包含搜索模式的所有sNumbers(例如“384”) 然后返回过滤后的序列,以便首先排序以搜索模式(“384”)开头的sNumbers,然后是在某处包含搜索模式的剩余sNumbers。所以它会是这样的(请注意组中的字母排序):

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

以下是我的开始:

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

所以现在我们拥有包含搜索模式的所有数字。这就是我被困的地方。我知道在这一点上我需要将结果“分组”成两个序列。一个以搜索模式开始,另一个不是。然后按字母顺序在每个组中应用辅助排序。如何编写一个结合了所有这些的查询?

7 个答案:

答案 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();

答案 1 :(得分: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和Contains分组到 g

2。)只需选择包含匹配的组

3。)通过StartsWith键的反转顺序(以便StartsWith = true在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 + " "));

答案 2 :(得分: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)));

答案 3 :(得分:2)

这似乎相当直接,除非我误解了一些事情:

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

我明白了:

outputlist

答案 4 :(得分:0)

Linq有一个OrderBy方法,允许您提供一个自定义类来决定应该如何排序。看这里: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();

答案 5 :(得分: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);

答案 6 :(得分:0)

对不起家伙,在阅读完回复后,我意识到我在我的问题中犯了一个错误。正确答案如下:(排序方式为&#34;以&#34开头;首先排序,然后按字母顺序排列(非数字排列)

//输出:{&#34; 38450&#34;,&#34; 38451&#34;,&#34; 13841&#34;,&#34; 138477&#34;,&#34; 28384&# 34;}

我能够通过以下查询实现这一目标:

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

由于