【问题标题】:Using LINQ to group numbers使用 LINQ 对数字进行分组
【发布时间】:2014-01-06 03:32:53
【问题描述】:

假设我有一个这样的数字列表,

   [3, 3, 1, 2, 3, 2]

我想将它们按顺序分组在一起,使每组的总和小于或等于五,即正确答案是:

  [3], [3, 1], [2,3], [2]

有没有办法用 Linq 来表达?

【问题讨论】:

    标签: c# linq functional-programming


    【解决方案1】:

    不知道这是否接近您的想法,但让我们试一试

    List<int> l = new List<int> { 3, 3, 1, 2, 3, 2 };
    int[] index = {0};
    var s = l.Select((k, i) =>
    {
        if (i < index[0])
            return null;
    
        int total = 0;
        return l.Skip(index[0]).TakeWhile(x =>
        {
            total += x;
            if (total <= 5)
                index[0]++;
            return total <= 5;
        });
    }).Where(x => x != null);
    
    foreach (IEnumerable<int> e in s)
    {
        foreach (int i in e)
        {
            Console.Write("{0},", i);
        }
        Console.WriteLine();
    }
    

    我的推理如下,

    1. 我们必须遍历所有项目,因此选择。
    2. 我们必须从列表中取出项目,直到达到总数,因此TakeWhile
    3. 我们必须跟踪我们在列表中的位置,因此索引和返回 null。

    我最初尝试使用int index = 0;,但Resharper 抱怨访问修改后的闭包并建议将其更改为int[] index = {0};

    【讨论】:

      【解决方案2】:

      简单。

      var results = xs.Aggregate<int, List<List<int>>>(
          new List<List<int>> { new List<int>() },
          (a, n) =>
          {
              if (a.Last().Sum() + n > 5)
              {
                  a.Add(new List<int> { n });
              }
              else
              {
                  a.Last().Add(n);
              }
              return a;
          });
      

      所以,从这里:

      var xs = new [] { 3, 3, 1, 2, 3, 2, };
      

      我明白了:

      【讨论】:

      • 非常非常狡猾。我喜欢。唯一潜在的缺点是每向前迈出一步,都会重复对 Last() 列表中的先前值求和。对于较大的目标组总和(例如 10000)可能会很慢,但对于目标为 5 的这种情况来说很好。
      【解决方案3】:

      您可以尝试一种扩展方法(类似这样的方法,用您的示例进行了测试,但没有比这更多的测试!):

      public static class Extensions
      {
          public static IEnumerable<IEnumerable<int>> GroupsLessThan(this IEnumerable<int> source, int target)
          {
              var list = new List<int>();
              var runningCount = 0;
              foreach (var element in source)
              {
                  if (runningCount + element < target)
                  {
                      list.Add(element);
                      runningCount += element;
                  }
                  else
                  {
                      yield return list;
                      runningCount = element;
                      list = new List<int>{element};
                  }
              }
      
              if (list.Count > 0)
              {
                  yield return list;
              }
          }
      }
      

      用法如下:

      var array = new int[] { 3, 3, 1, 2, 3, 2 };
      var list = array.GroupsLessThan(6).ToList();
      

      【讨论】:

        猜你喜欢
        • 2016-01-16
        • 1970-01-01
        • 2015-06-05
        • 1970-01-01
        • 2013-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-27
        相关资源
        最近更新 更多