我有一個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