【问题标题】:LINQ query that combines grouping and sorting组合分组和排序的 LINQ 查询
【发布时间】:2016-05-24 05:53:01
【问题描述】:

我对@9​​87654321@ 比较陌生,目前正在研究一个结合了分组和排序的查询。我将从这里的一个例子开始。基本上我有一个表示为字符串的任意数字序列:

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

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

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

我是这样开始的:

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

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

【问题讨论】:

  • 对不起,看完回复后,我意识到我犯了上面的错误。正确答案如下:(按开头排序,然后按字母顺序(非数字){“38450”、“38451”、“13841”、“138477”、“28384”}

标签: c# linq list sorting


【解决方案1】:

抱歉各位,看完回复后,我意识到我的问题有误。正确答案如下:(首先按“开头”排序,然后按字母顺序(不是数字)

// 输出:{“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();

谢谢

【讨论】:

    【解决方案2】:

    这似乎相当简单,除非我误解了什么:

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

    我明白了:

    【讨论】:

    • 唯一的问题是二级排序是按字母顺序排列的,而不是数字。我可以通过以下代码实现这一点: List result = sNumbers .Where(n => n.Contains(searchpattern)) .OrderBy(s => !s.StartsWith(searchpattern)) .ThenBy(s => s) .ToList();感谢您的帮助
    【解决方案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();
    

    【讨论】:

      【解决方案4】:

      这里是优化版,只需要一个 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 + " "));
      

      【讨论】:

      • 感谢您的帮助。我能够使用扩展方法语法通过更简单的解决方案来实现它。菲克
      【解决方案5】:
      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)));
      

      【讨论】:

      • 这不会按数字排序
      • @MarkusWeninger,是的,你是对的,已修复,谢谢
      • 您的回答很有道理,但我们是按字母顺序排序,而不是按数字排序
      • @FikeRehman,然后将 .OrderBy(e => Int32.Parse(e)) 替换为 .OrderBy(e => e) 完整示例: var result = sNumbers .Where(e => e. StartsWith("384")) .OrderBy(e => e) .Union(sNumbers .Where(e => e.Contains("384")) .OrderBy(e => e));
      【解决方案6】:

      您可以创建一个列表,其中包含以 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);
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • 您必须删除 n.Contains(searchPattern)。如果你不这样做,你只会使用一个子集
        • 据我了解,这就是他想要的:“我需要在此列表中找到所有包含搜索模式(例如“384”)的 sNumber,然后返回过滤后的序列,以便开始的 sNumber具有搜索模式 ("384") 的优先排序。"
        猜你喜欢
        • 1970-01-01
        • 2021-10-31
        • 2016-08-01
        • 1970-01-01
        • 2014-10-14
        • 2015-06-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多