【问题标题】:LINQ: GroupBy with maximum count in each groupLINQ:GroupBy,每组中的最大计数
【发布时间】:2010-06-04 07:53:09
【问题描述】:

我有一个重复数字列表:

Enumerable.Range(1,3).Select(o => Enumerable.Repeat(o, 3)).SelectMany(o => o)
// {1,1,1,2,2,2,3,3,3}

我将它们分组并获得出现的数量:

Enumerable.Range(1,3).Select(o => Enumerable.Repeat(o, 3)).SelectMany(o => o)
    .GroupBy(o => o).Select(o => new { Qty = o.Count(), Num = o.Key })

Qty   Num
3     1
3     2
3     3

我真正需要的是将每组的数量限制为某个数字。如果限制为 2,则上述分组的结果将是:

Qty   Num
2     1
1     1
2     2
1     2
2     3
1     3

因此,如果 Qty = 10 且限制为 4,则结果为 3 行(4、4、2)。每个数字的数量并不像示例中那样相等。整个列表的指定数量限制相同(不因数量而异)。

谢谢

【问题讨论】:

  • 我只是好奇。这个算法是干什么用的?
  • 我需要以这种格式为 CNC 机床输出数据。

标签: linq


【解决方案1】:

其他一些答案使 LINQ 查询比它需要的复杂得多。使用foreach 循环当然更快、更高效,但 LINQ 替代方案仍然相当简单。

var input = Enumerable.Range(1, 3).SelectMany(x => Enumerable.Repeat(x, 10));
int limit = 4;

var query =
    input.GroupBy(x => x)
         .SelectMany(g => g.Select((x, i) => new { Val = x, Grp = i / limit }))
         .GroupBy(x => x, x => x.Val)
         .Select(g => new { Qty = g.Count(), Num = g.Key.Val });

【讨论】:

    【解决方案2】:

    最近出现了一个similar question,询问如何在 SQL 中执行此操作 - 没有真正优雅的解决方案,除非这是 Linq to SQL 或实体框架(即被翻译成 SQL 查询),我真的建议您不要尝试用 Linq 解决这个问题,而是编写一个迭代解决方案;它将变得更加高效且易于维护。

    也就是说,如果您绝对必须使用基于集合(“Linq”)的方法,这是您可以做到的一种方法:

    var grouped =
        from n in nums
        group n by n into g
        select new { Num = g.Key, Qty = g.Count() };
    
    int maxPerGroup = 2;
    var portioned =
        from x in grouped
        from i in Enumerable.Range(1, grouped.Max(g => g.Qty))
        where (x.Qty % maxPerGroup) == (i % maxPerGroup)
        let tempQty = (x.Qty / maxPerGroup) == (i / maxPerGroup) ? 
            (x.Qty % maxPerGroup) : maxPerGroup
        select new
        {
            Num = x.Num,
            Qty = (tempQty > 0) ? tempQty : maxPerGroup
        };
    

    与更简单更快的迭代版本比较:

    foreach (var g in grouped)
    {
        int remaining = g.Qty;
        while (remaining > 0)
        {
            int allotted = Math.Min(remaining, maxPerGroup);
            yield return new MyGroup(g.Num, allotted);
            remaining -= allotted;
        }
    }
    

    【讨论】:

    • 你说得对,LINQ 方法太复杂了。谢谢。
    【解决方案3】:

    Aaronaught 的出色答案并未涵盖两全其美的可能性...使用扩展方法提供迭代解决方案。

    未经测试:

    public static IEnumerable<IEnumerable<U>> SplitByMax<T, U>(
      this IEnumerable<T> source,
      int max,
      Func<T, int> maxSelector,
      Func<T, int, U> resultSelector
    )
    {
      foreach(T x in source)
      {
        int number = maxSelector(x);
        List<U> result = new List<U>();
        do
        {
          int allotted = Math.Min(number, max); 
          result.Add(resultSelector(x, allotted));
          number -= allotted
        } while (number > 0 && max > 0);
    
        yield return result;
      }
    }
    

    调用者:

    var query = grouped.SplitByMax(
      10,
      o => o.Qty,
      (o, i) => new {Num = o.Num, Qty = i}
    )
    .SelectMany(split => split);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-10
      • 2014-04-13
      相关资源
      最近更新 更多