如何将IEnumerable <String>拆分为IEnumerable <string>组
我有一个IEnumerable<string
>,我想分成三个组,如果我的input有6个项目,我会得到一个IEnumerable<IEnumerable<string>>
返回两个项目,其中每个将包含一个IEnumerable<string>
哪我的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>>
,它的function类似于你想要的。 但是,如果您严格按照IEnumerable<IEnumerable<string>>
(要传递给不支持generics差异的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>>();
这是对这个线程的延迟回复,但是这里是一个不使用任何临时存储的方法:
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()); } }
还有一些testing代码:
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(); } }
我知道这已经被回答了,但是如果你打算经常使用IEnumerables的片断,那么我build议使用这样的通用扩展方法:
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'已经被定义为string,'Split'就是我刚才所说的。
启发由@ dicegiuy30的实现,我想创build一个版本,只遍历源一次,并不build立在内存中的整个结果集来弥补。 最好的我想出来的是这样的:
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; } }
这样我就可以按需build立每个块。 我希望我也应该避开List<T>
,并且只是把这个stream传下去,但还没有弄清楚。
使用Microsoft.Reactive你可以很简单地做到这一点,你只会遍历源代码一次。
IEnumerable<string> source = new List<string>{"1", "2", "3", "4", "5", "6"}; IEnumerable<IEnumerable<string>> splited = source.ToObservable().Buffer(3).ToEnumerable();
我们可以改进@ Afshari的解决scheme来做真正的懒惰评估。 我们使用一个GroupAdjacentBy
方法来产生具有相同键的连续元素组:
sequence .Select((x, i) => new { Value = x, Index = i }) .GroupAdjacentBy(x=>x.Index/3) .Select(g=>g.Select(x=>x.Value))
因为这些组是一个接一个地产生的,所以这个解决scheme可以有效地处理长序列或无限序列。
我想出了一个不同的方法。 它使用了一个while
迭代器,但结果像常规的LINQ一样caching在内存中直到需要。
这是代码。
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; }
Mehrdad 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 => xi/size, x => xv); } }