我有一个重复的数字列表:
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且limit为4,则结果为3行(4,4,2)。每个数字的数量与示例中的不相等。整个列表的指定数量限制是相同的(根据数量不同)。
由于
答案 0 :(得分:4)
最近有一个similar question问到如何在SQL中执行此操作 - 没有非常优雅的解决方案,除非这是Linq to SQL或Entity Framework(即被翻译成SQL查询),我' d真的建议你不尝试用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;
}
}
答案 1 :(得分:3)
其他一些答案使得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 :(得分:0)
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);