我有一个IEnumerable<string
>我想分成三组,所以如果我输入了6个项目,我会得到一个IEnumerable<IEnumerable<string>>
与每一个两个项目返回的结果包含我的字符串内容的IEnumerable<string>
。我如何拆分一个IEnumerable <String>到IEnumerable的组<string>
我正在寻找如何做到这一点的LINQ,而不是一个简单的for循环
感谢
我有一个IEnumerable<string
>我想分成三组,所以如果我输入了6个项目,我会得到一个IEnumerable<IEnumerable<string>>
与每一个两个项目返回的结果包含我的字符串内容的IEnumerable<string>
。我如何拆分一个IEnumerable <String>到IEnumerable的组<string>
我正在寻找如何做到这一点的LINQ,而不是一个简单的for循环
感谢
var result = sequence.Select((s, i) => new { Value = s, Index = i })
.GroupBy(item => item.Index/3, item => item.Value);
注意这将返回一个IEnumerable<IGrouping<int,string>>
这将是功能上类似于你想要什么。但是,如果严格需要将其类型为IEnumerable<IEnumerable<string>>
(传递给需要它的方法C#3.0不支持泛型方差),你应该使用Enumerable.Cast
:
var result = sequence.Select((s, i) => new { Value = s, Index = i })
.GroupBy(item => item.Index/3, item => item.Value)
.Cast<IEnumerable<string>>();
我想出了一个不同做法。它使用了一个while
迭代器,但结果像常规LINQ一样缓存在内存中,直到需要为止。
这是代码。
public IEnumerable<IEnumerable<T>> Paginate<T>(this IEnumerable<T> source, int pageSize)
{
List<IEnumerable<T>> pages = new List<IEnumerable<T>>();
int skipCount = 0;
while (skipCount * pageSize < source.Count) {
pages.Add(source.Skip(skipCount * pageSize).Take(pageSize));
skipCount += 1;
}
return pages;
}
之前从未见过'Runtime.CompilerServices.Extension',所以我查了一下,MSDN说:“在C#中,你不需要使用这个属性;你应该使用'this'修饰符作为第一个参数来创建一个扩展方法。“换句话说,虽然它在功能上等价,但最好使用'public IEnumerable
@ Davy8:我刚刚在学习C#时提交了这个答案,所以代码是VB.NET的一个直接端口。我现在已经掌握了一些关于C#的知识,并且我知道这段代码并不是真正的“正确”。我已经更新了答案。 – 2011-07-06 02:54:09
你是怎么得到'source.Count'的?它是一个可枚举的,并且在其上执行'Count()'枚举集合。 – nawfal 2013-02-18 14:06:35
我知道这已经回答了,但如果你打算采取IEnumerables切片的时候,那么我建议做一个通用的扩展方法是这样的:
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkSize)
{
return source.Where((x,i) => i % chunkSize == 0).Select((x,i) => source.Skip(i * chunkSize).Take(chunkSize));
}
然后你可以使用sequence.Split(3)
来得到你想要的。 (如果你不喜欢这个,你可以命名为'slice'或'chunk','split'已经被定义为字符串,'Split'就是我碰巧称为我的)。
+1。我喜欢这样一个事实,即您可以获得与我使用一行代码相同的结果。 – 2010-10-14 16:53:05
这已经有一段时间了,我一直在使用你的代码(解决方案/答案/无论你怎么称呼它)一段时间,它完美的工作。我最近试图分析你的代码,我无法掌握'.Where((x,i)=> i%chunkSize == 0)'代码的一部分,它仍然可以正常工作。如果你不介意,你能解释一下你的代码对我有什么作用吗?谢谢。 – 2011-05-05 18:56:41
@Alex当然!假设你的收藏有9个项目的长度,你想把它分成3个小组。所有这些表达的确是确定将要制作多少个小组。正如你所看到的,我只对'Where'和'Select'中的索引真正感兴趣。因为'Where'子句只会从9个项目中返回3个项目(Enumerable.Range的检查结果),因此我将'Where'中的索引'0-8'设置为'Select'中的'0-2' (0,9)。选择((x,i)=> i%3)'作为证明!)。所以我首先跳过0(0 * 3)并取3,然后跳过3(1 * 3)并取3然后跳过6(2 * 3)并取3! – diceguyd30 2011-05-05 20:08:18
启发通过@ dicegiuy30的实现,我想创建一个只迭代源的版本,并且不会在内存中构建整个结果集来进行补偿。最好的我想出的是这样的:
public static IEnumerable<IEnumerable<T>> Split2<T>(this IEnumerable<T> source, int chunkSize) {
var chunk = new List<T>(chunkSize);
foreach(var x in source) {
chunk.Add(x);
if(chunk.Count <= chunkSize) {
continue;
}
yield return chunk;
chunk = new List<T>(chunkSize);
}
if(chunk.Any()) {
yield return chunk;
}
}
这样我就建立每个块的需求。我希望我也应该避开List<T>
,并且只是传播那个,但还没有弄清楚。
+1是一个很好的实现。与Jon Skeet完全一样:http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/Batch.cs – diceguyd30 2012-02-06 14:05:33
+1这似乎是非常有效的,但我认为它有一个错误代码如下:'if(chunk.Count <= chunkSize)' 正确的代码行如下:'if(chunk.Count
使用Microsoft.Reactive你可以很简单地做到这一点,你将只遍历源代码一次。
IEnumerable<string> source = new List<string>{"1", "2", "3", "4", "5", "6"};
IEnumerable<IEnumerable<string>> splited = source.ToObservable().Buffer(3).ToEnumerable();
您不需要使用ToObservable,然后返回ToEnumerable ,您可以使用与Enumerable一起使用的交互式扩展缓冲区方法。在nuget上查找Ix-Main。 – 2013-10-23 04:10:00
如果使用'Reactive',则需要ToObservable。否则,您必须使用'Interactive' – Emaborsa 2016-12-22 09:18:08
我们可以改进@ Afshari的解决方案来做真正的懒惰评估。我们使用GroupAdjacentBy
方法能产生连续的元素组具有相同的键:
sequence
.Select((x, i) => new { Value = x, Index = i })
.GroupAdjacentBy(x=>x.Index/3)
.Select(g=>g.Select(x=>x.Value))
由于基团,得到一个接一个,该解决方案的工作原理有效地与长或无限序列。
这是一个迟到的回复此线程,但这里是不使用任何临时存储的方法:
public static class EnumerableExt
{
public static IEnumerable<IEnumerable<T>> Partition<T>(this IEnumerable<T> input, int blockSize)
{
var enumerator = input.GetEnumerator();
while (enumerator.MoveNext())
{
yield return nextPartition(enumerator, blockSize);
}
}
private static IEnumerable<T> nextPartition<T>(IEnumerator<T> enumerator, int blockSize)
{
do
{
yield return enumerator.Current;
}
while (--blockSize > 0 && enumerator.MoveNext());
}
}
和一些测试代码:
class Program
{
static void Main(string[] args)
{
var someNumbers = Enumerable.Range(0, 10000);
foreach (var block in someNumbers.Partition(100))
{
Console.WriteLine("\nStart of block.");
foreach (int number in block)
{
Console.Write(number);
Console.Write(" ");
}
}
Console.WriteLine("\nDone.");
Console.ReadLine();
}
}
迈赫达德Afshari的答案是优秀。这里是一个封装它的扩展方法:
using System.Collections.Generic;
using System.Linq;
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> GroupsOf<T>(this IEnumerable<T> enumerable, int size)
{
return enumerable.Select((v, i) => new {v, i}).GroupBy(x => x.i/size, x => x.v);
}
}
这是unbelivably快,感谢 – 2009-08-28 21:36:39
是否具有的GroupBy遍历整个序列,你得到任何结果之前,或者你还在这里得到延迟执行? – 2009-11-14 00:27:34
@Don Kirkby:对于LINQ to Objects,'.GroupBy'不会枚举序列。它一旦枚举了'.GetEnumerator'就会枚举整个序列(例如,在'foreach'中使用的时候)。 – 2009-11-14 06:19:42