如何获取一个列表(使用 LINQ)并将其分解为一个列表列表,并在每 8 个条目上对原始列表进行分区?
我想像这样的事情会涉及 Skip 和/或 Take,但我对 LINQ 还很陌生。
编辑:使用 C# / .Net 3.5
Edit2:这个问题的措辞与其他“重复”问题不同。虽然问题很相似,但这个问题的答案更好:“接受”的答案都非常可靠(带有 yield
语句),以及 Jon Skeet 使用 MoreLinq 的建议(不推荐)在“其他”问题中。)有时重复是好的,因为它们迫使重新检查问题。
How would one take a List (using LINQ) and break it into a List of Lists partitioning the original list on every 8th entry?
I imagine something like this would involve Skip and/or Take, but I'm still pretty new to LINQ.
Edit: Using C# / .Net 3.5
Edit2: This question is phrased differently than the other "duplicate" question. Although the problems are similar, the answers in this question are superior: Both the "accepted" answer is very solid (with the yield
statement) as well as Jon Skeet's suggestion to use MoreLinq (which is not recommended in the "other" question.) Sometimes duplicates are good in that they force a re-examination of a problem.
发布评论
评论(7)
使用以下扩展方法将输入分解为子集
Use the following extension method to break the input into subsets
我们在 MoreLINQ 中有这样一个方法作为 批处理方法:
或
We have just such a method in MoreLINQ as the Batch method:
or
您最好使用 MoreLinq 这样的库,但如果您确实必须使用“ plain LINQ”,您可以使用
GroupBy
:基本上,我们使用
Select()
的版本,它为正在使用的值提供索引,我们将索引以 8 来标识每个值属于哪个组。然后我们通过这个分组键对序列进行分组。最后一个Select
只是将IGrouping<>
简化为IEnumerable>
(并且并不是严格必要的,因为IGrouping
是一个IEnumerable
)。通过分解示例中的常量
8
并将其替换为指定的参数,可以很容易地将其转换为可重用的方法。它不一定是最优雅的解决方案,也不再是一个懒惰的流式解决方案……但它确实有效。
您还可以使用迭代器块 (
yield return
) 编写自己的扩展方法,这可以为您提供比GroupBy
更好的性能并使用更少的内存。这就是 MoreLinq 的Batch()
方法的 IIRC 功能。You're better off using a library like MoreLinq, but if you really had to do this using "plain LINQ", you can use
GroupBy
:Basically, we use the version of
Select()
that provides an index for the value being consumed, we divide the index by 8 to identify which group each value belongs to. Then we group the sequence by this grouping key. The lastSelect
just reduces theIGrouping<>
down to anIEnumerable<IEnumerable<T>>
(and isn't strictly necessary sinceIGrouping
is anIEnumerable
).It's easy enough to turn this into a reusable method by factoring our the constant
8
in the example, and replacing it with a specified parameter.It's not necessarily the most elegant solution, and it is not longer a lazy, streaming solution ... but it does work.
You could also write your own extension method using iterator blocks (
yield return
) which could give you better performance and use less memory thanGroupBy
. This is what theBatch()
method of MoreLinq does IIRC.这根本不是 Linq 最初设计者的初衷,但请看看 GroupBy 的误用:
我认为它赢得了这个问题的“高尔夫”奖。
ToList
非常重要,因为您希望在尝试对输出执行任何操作之前确保已实际执行分组。如果删除ToList
,您会得到一些奇怪的副作用。It's not at all what the original Linq designers had in mind, but check out this misuse of GroupBy:
I think it wins the "golf" prize for this question. The
ToList
is very important since you want to make sure the grouping has actually been performed before you try doing anything with the output. If you remove theToList
, you will get some weird side effects.Take 的效率不会很高,因为它不会删除已获取的条目。
为什么不使用一个简单的循环:
Take won't be very efficient, because it doesn't remove the entries taken.
why not use a simple loop:
Mel 给出了最简单的解决方案:
简洁但速度较慢。上述方法将 IEnumerable 拆分为所需固定大小的块,块的总数并不重要。要将 IEnumerable 拆分为 N 个大小相等或接近相等大小的块,您可以执行以下
操作: 为了加快速度,可以使用一种简单的方法:
这比目前地球上的任何东西都快:) < code>Split 操作此处
The simplest solution is given by Mel:
Concise but slower. The above method splits an IEnumerable into chunks of desired fixed size with total number of chunks being unimportant. To split an IEnumerable into N number of chunks of equal sizes or close to equal sizes, you could do:
To speed up things, a straightforward approach would do:
This is faster than anything currently on planet now :) The equivalent methods for a
Split
operation here