जवाबों:
आपको कोई कोड लिखने की आवश्यकता नहीं है। MoreLINQ बैच विधि का उपयोग करें , जो स्रोत अनुक्रम को आकार की बाल्टियों में बैचती है (MoreLINQ एक NuGet पैकेज के रूप में उपलब्ध है जिसे आप स्थापित कर सकते हैं):
int size = 10;
var batches = sequence.Batch(size);
जिसे इस प्रकार लागू किया गया है:
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source, int size)
{
TSource[] bucket = null;
var count = 0;
foreach (var item in source)
{
if (bucket == null)
bucket = new TSource[size];
bucket[count++] = item;
if (count != size)
continue;
yield return bucket;
bucket = null;
count = 0;
}
if (bucket != null && count > 0)
yield return bucket.Take(count).ToArray();
}
Batch(new int[] { 1, 2 }, 1000000)
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
int maxItems)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / maxItems)
.Select(g => g.Select(x => x.item));
}
}
और उपयोग होगा:
List<int> list = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach(var batch in list.Batch(3))
{
Console.WriteLine(String.Join(",",batch));
}
उत्पादन:
0,1,2
3,4,5
6,7,8
9
GroupBy
गणना शुरू करने के बाद , क्या इसके स्रोत को पूरी तरह से गणना करने की आवश्यकता नहीं है? यह स्रोत का आलसी मूल्यांकन खो देता है और इस प्रकार, कुछ मामलों में, बैचिंग के लाभ के सभी!
यदि आप sequence
एक के रूप में परिभाषित के साथ शुरू करते हैं IEnumerable<T>
, और आप जानते हैं कि यह कई बार सुरक्षित रूप से एनुमरेट किया जा सकता है (जैसे कि यह एक सरणी या सूची है), तो आप बस बैचों में तत्वों को संसाधित करने के लिए इस सरल पैटर्न का उपयोग कर सकते हैं:
while (sequence.Any())
{
var batch = sequence.Take(10);
sequence = sequence.Skip(10);
// do whatever you need to do with each batch here
}
उपरोक्त सभी बड़े बैच या कम मेमोरी स्पेस के साथ बहुत अच्छा प्रदर्शन करते हैं। अपनी खुद की लिखना होगा कि पाइपलाइन (कहीं भी कोई आइटम संचय नोटिस नहीं):
public static class BatchLinq {
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (IEnumerator<T> enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return TakeIEnumerator(enumerator, size);
}
private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
int i = 0;
do
yield return source.Current;
while (++i < size && source.MoveNext());
}
}
संपादित करें: इस दृष्टिकोण के साथ ज्ञात समस्या यह है कि प्रत्येक बैच को अगले बैच में जाने से पहले पूरी तरह से एन्यूमरेट और एनुमरेट किया जाना चाहिए। उदाहरण के लिए यह काम नहीं करता है:
//Select first item of every 100 items
Batch(list, 100).Select(b => b.First())
यह एक पूरी तरह से आलसी, कम ओवरहेड, बैच का एक-फंक्शन कार्यान्वयन है जो कोई संचय नहीं करता है। EricRoller की मदद से Nick Whaley के समाधान पर आधारित (और समस्याओं को हल करता है) ।
पुनरावृत्ति अंतर्निहित IEnumerable से सीधे आती है, इसलिए तत्वों को सख्त क्रम में गणना की जानी चाहिए, और एक से अधिक बार एक्सेस नहीं किया जाना चाहिए। यदि कुछ तत्वों का आंतरिक लूप में उपभोग नहीं किया जाता है, तो उन्हें छोड़ दिया जाता है (और एक पुनरावृत्त चलने वाले के माध्यम से उन्हें फिर से एक्सेस करने की कोशिश कर रहा है InvalidOperationException: Enumeration already finished.
)।
आप .NET फिडल पर एक पूर्ण नमूने का परीक्षण कर सकते हैं ।
public static class BatchLinq
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
{
int i = 0;
// Batch is a local function closing over `i` and `enumerator` that
// executes the inner batch enumeration
IEnumerable<T> Batch()
{
do yield return enumerator.Current;
while (++i < size && enumerator.MoveNext());
}
yield return Batch();
while (++i < size && enumerator.MoveNext()); // discard skipped items
}
}
}
done
हमेशा के e.Count()
बाद कॉल करके चेक को समाप्त कर सकते हैं yield return e
। आप अपरिभाषित व्यवहार आह्वान नहीं करने के लिए BatchInner में पाश को पुनर्व्यवस्थित करने की आवश्यकता होगी source.Current
, तो i >= size
। यह BatchInner
प्रत्येक बैच के लिए एक नया आवंटित करने की आवश्यकता को समाप्त कर देगा ।
i
इसलिए यह एक अलग वर्ग को परिभाषित करने की तुलना में अधिक कुशल नहीं है, लेकिन मुझे लगता है कि यह थोड़ा क्लीनर है।
मुझे आश्चर्य है कि क्यों किसी ने कभी भी एक पुराने स्कूल के लिए लूप समाधान पोस्ट नहीं किया है। यहाँ एक है:
List<int> source = Enumerable.Range(1,23).ToList();
int batchsize = 10;
for (int i = 0; i < source.Count; i+= batchsize)
{
var batch = source.Skip(i).Take(batchsize);
}
यह सादगी संभव है क्योंकि टेक विधि:
...
source
तत्वों की पैदावार होने तकcount
तत्वों की गणना और पैदावार करता है याsource
इसमें अधिक तत्व नहीं होते हैं। यदिcount
तत्वों की संख्या से अधिक हैsource
, तो सभी तत्वsource
वापस आ जाते हैं
अस्वीकरण:
लूप के अंदर स्किप और टेक का उपयोग करने का मतलब है कि एन्यूमरेबल को कई बार एन्यूमरेट किया जाएगा। यह खतरनाक है अगर गणना करने योग्य को स्थगित कर दिया जाता है। इसके परिणामस्वरूप डेटाबेस क्वेरी, या वेब अनुरोध या फ़ाइल पढ़ने के कई निष्पादन हो सकते हैं। यह उदाहरण स्पष्ट रूप से एक सूची के उपयोग के लिए है जिसे स्थगित नहीं किया गया है, इसलिए यह किसी समस्या से कम नहीं है। यह अभी भी एक धीमी गति से समाधान है क्योंकि स्किप हर बार जब यह कहा जाता है तो संग्रह को फिर से जोड़ देगा।
यह GetRange
विधि का उपयोग करके भी हल किया जा सकता है , लेकिन इसे एक संभावित रेस्ट बैच निकालने के लिए एक अतिरिक्त गणना की आवश्यकता होती है:
for (int i = 0; i < source.Count; i += batchsize)
{
int remaining = source.Count - i;
var batch = remaining > batchsize ? source.GetRange(i, batchsize) : source.GetRange(i, remaining);
}
यहां इसे संभालने का तीसरा तरीका है, जो 2 छोरों के साथ काम करता है। यह सुनिश्चित करता है कि संग्रह केवल 1 बार ही एन्यूमरेट किया गया हो! "
int batchsize = 10;
List<int> batch = new List<int>(batchsize);
for (int i = 0; i < source.Count; i += batchsize)
{
// calculated the remaining items to avoid an OutOfRangeException
batchsize = source.Count - i > batchsize ? batchsize : source.Count - i;
for (int j = i; j < i + batchsize; j++)
{
batch.Add(source[j]);
}
batch.Clear();
}
Skip
और Take
अंदर का मतलब है कि एन्यूमरेबल को कई बार एन्यूमरेट किया जाएगा। यह खतरनाक है अगर गणना करने योग्य को स्थगित कर दिया जाता है। इसके परिणामस्वरूप डेटाबेस क्वेरी, या वेब अनुरोध या फ़ाइल पढ़ने के कई निष्पादन हो सकते हैं। आपके उदाहरण में आपके पास एक है List
जो स्थगित नहीं है, इसलिए यह एक समस्या से कम नहीं है।
MoreLINQ के रूप में एक ही दृष्टिकोण, लेकिन सरणी के बजाय सूची का उपयोग करना। मैंने बेंचमार्किंग नहीं की है, लेकिन कुछ लोगों के लिए पठनीयता अधिक मायने रखती है:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
List<T> batch = new List<T>();
foreach (var item in source)
{
batch.Add(item);
if (batch.Count >= size)
{
yield return batch;
batch.Clear();
}
}
if (batch.Count > 0)
{
yield return batch;
}
}
size
करने के लिए पैरामीटर में पास करें new List
।
batch.Clear();
के साथbatch = new List<T>();
यहाँ Nick Whaley's ( लिंक ) और infogulch's ( लिंक ) आलसी Batch
कार्यान्वयन का एक बेहतर सुधार है । यह सख्त है। आप या तो सही क्रम में बैचों की गणना करते हैं, या आपको अपवाद मिलता है।
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source, int size)
{
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
using (var enumerator = source.GetEnumerator())
{
int i = 0;
while (enumerator.MoveNext())
{
if (i % size != 0) throw new InvalidOperationException(
"The enumeration is out of order.");
i++;
yield return GetBatch();
}
IEnumerable<TSource> GetBatch()
{
while (true)
{
yield return enumerator.Current;
if (i % size == 0 || !enumerator.MoveNext()) break;
i++;
}
}
}
}
और यहाँ Batch
प्रकार के स्रोतों के लिए एक आलसी कार्यान्वयन है IList<T>
। यह गणना पर कोई प्रतिबंध नहीं लगाता है। बैचों को किसी भी क्रम में, और एक से अधिक बार आंशिक रूप से गणना की जा सकती है। गणना के दौरान संग्रह को संशोधित नहीं करने का प्रतिबंध अभी भी लागू है। यह enumerator.MoveNext()
किसी भी चंक या तत्व की उपज से पहले डमी कॉल करके हासिल किया जाता है । नकारात्मक पक्ष यह है कि प्रगणक को अपरिष्कृत छोड़ दिया जाता है, क्योंकि यह अज्ञात है जब संलयन समाप्त होने जा रहा है।
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IList<TSource> source, int size)
{
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
var enumerator = source.GetEnumerator();
for (int i = 0; i < source.Count; i += size)
{
enumerator.MoveNext();
yield return GetChunk(i, Math.Min(i + size, source.Count));
}
IEnumerable<TSource> GetChunk(int from, int toExclusive)
{
for (int j = from; j < toExclusive; j++)
{
enumerator.MoveNext();
yield return source[j];
}
}
}
मैं इसमें बहुत देर से शामिल हो रहा हूं लेकिन मुझे कुछ और दिलचस्प लगा।
इसलिए हम यहां Skip
और Take
बेहतर प्रदर्शन के लिए उपयोग कर सकते हैं ।
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int maxItems)
{
return items.Select((item, index) => new { item, index })
.GroupBy(x => x.index / maxItems)
.Select(g => g.Select(x => x.item));
}
public static IEnumerable<T> Batch2<T>(this IEnumerable<T> items, int skip, int take)
{
return items.Skip(skip).Take(take);
}
}
आगे मैंने 100000 रिकॉर्ड के साथ जाँच की। लूपिंग केवल के मामले में अधिक समय ले रहा हैBatch
कंसोल एप्लिकेशन का कोड।
static void Main(string[] args)
{
List<string> Ids = GetData("First");
List<string> Ids2 = GetData("tsriF");
Stopwatch FirstWatch = new Stopwatch();
FirstWatch.Start();
foreach (var batch in Ids2.Batch(5000))
{
// Console.WriteLine("Batch Ouput:= " + string.Join(",", batch));
}
FirstWatch.Stop();
Console.WriteLine("Done Processing time taken:= "+ FirstWatch.Elapsed.ToString());
Stopwatch Second = new Stopwatch();
Second.Start();
int Length = Ids2.Count;
int StartIndex = 0;
int BatchSize = 5000;
while (Length > 0)
{
var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
// Console.WriteLine("Second Batch Ouput:= " + string.Join(",", SecBatch));
Length = Length - BatchSize;
StartIndex += BatchSize;
}
Second.Stop();
Console.WriteLine("Done Processing time taken Second:= " + Second.Elapsed.ToString());
Console.ReadKey();
}
static List<string> GetData(string name)
{
List<string> Data = new List<string>();
for (int i = 0; i < 100000; i++)
{
Data.Add(string.Format("{0} {1}", name, i.ToString()));
}
return Data;
}
समय ऐसा हो गया।
पहला - 00: 00: 00.0708, 00: 00: 00.0660
दूसरा (टेक एंड स्किप वन) - 00: 00: 00.0008, 00: 00: 00.0008
GroupBy
पूरी तरह से इससे पहले कि यह एक पंक्ति पैदा करता है enumerates। यह बैचिंग करने का अच्छा तरीका नहीं है।
foreach (var batch in Ids2.Batch(5000))
को बदलना var gourpBatch = Ids2.Batch(5000)
और जांचना। var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
अगर समय में बदलाव के लिए आपके परिणाम हैं, तो मुझे इसमें दिलचस्पी लेनी होगी।
तो एक कार्यात्मक टोपी के साथ, यह तुच्छ प्रतीत होता है .... लेकिन सी # में, कुछ महत्वपूर्ण डाउनसाइड हैं।
आप शायद इसे IEnumerable (इसे Google के रूप में प्रकट करेंगे और आप संभवतः कुछ हास्केल डॉक्स में समाप्त कर देंगे, लेकिन हो सकता है कि कुछ # F # सामान का उपयोग किया जाए, यदि आप F # जानते हैं, तो Haskell डॉक्स पर स्क्विंट और यह बना देगा समझ)।
अनफोल्ड का संबंध आईईएन्यूमेरेरेबल के माध्यम से पुनरावृत्ति करने के बजाय फोल्ड ("एग्रीगेट") से है, यह आउटपुट डेटा स्ट्रक्चर्स के माध्यम से पुनरावृत्ति करता है (इसके आईएनम्यूएरेबल और आईओब्सर्वेबल के बीच एक समान संबंध है, वास्तव में मुझे लगता है कि आईओब्जर्वेबल एक "अनफोल्ड" जेनरेट करता है)। ..)
वैसे भी पहले आपको एक अनकही विधि की आवश्यकता है, मुझे लगता है कि यह काम करता है (दुर्भाग्य से यह अंततः बड़ी "सूचियों" के लिए स्टैक को उड़ा देगा ... आप इसे सुरक्षित रूप से एफ # उपज का उपयोग करके लिख सकते हैं!
static IEnumerable<T> Unfold<T, U>(Func<U, IEnumerable<Tuple<U, T>>> f, U seed)
{
var maybeNewSeedAndElement = f(seed);
return maybeNewSeedAndElement.SelectMany(x => new[] { x.Item2 }.Concat(Unfold(f, x.Item1)));
}
यह थोड़ा विरोधाभास है क्योंकि C # उन कुछ चीज़ों को लागू नहीं करता है जिन्हें कार्यात्मक लैंसग्यूज़ ने अनुमति दी है ... लेकिन यह मूल रूप से एक बीज लेता है और फिर IEnumerable में अगले तत्व का एक "हो सकता है" उत्तर और अगला बीज (हो सकता है) C # में मौजूद नहीं है, इसलिए हमने IEnumerable का उपयोग इसे नकली करने के लिए किया है), और शेष उत्तर को संक्षिप्त करता है (मैं इस "O (n?)" की जटिलता के लिए वाउच नहीं कर सकता)।
एक बार जब आप ऐसा कर लेते हैं;
static IEnumerable<IEnumerable<T>> Batch<T>(IEnumerable<T> xs, int n)
{
return Unfold(ys =>
{
var head = ys.Take(n);
var tail = ys.Skip(n);
return head.Take(1).Select(_ => Tuple.Create(tail, head));
},
xs);
}
यह सब काफी साफ दिखता है ... आप "n" तत्वों को IEnumerable में "अगले" तत्व के रूप में लेते हैं, और "पूंछ" बाकी अप्रमाणित सूची में है।
अगर सिर में कुछ नहीं है ... आप खत्म हो चुके हैं ... आप "कुछ भी नहीं" (लेकिन एक खाली IEnumerable> के रूप में नकली) वापस करते हैं ... अन्यथा आप प्रक्रिया करने के लिए सिर तत्व और पूंछ वापस करते हैं।
आप शायद IObservable का उपयोग करके ऐसा कर सकते हैं, वहाँ शायद "बैच" विधि पहले से ही है, और आप शायद इसका उपयोग कर सकते हैं।
यदि स्टैक ओवरफ्लो का खतरा बढ़ जाता है (यह शायद चाहिए), तो आपको एफ # में लागू करना चाहिए (और शायद इसके साथ कुछ एफ # लाइब्रेरी (एफएसएचआरपीएक्स?) पहले से ही है)।
(मैंने केवल इसके कुछ अल्पविकसित परीक्षण किए हैं, इसलिए इसमें अजीब तरह के कीड़े हो सकते हैं)।
मैंने एक कस्टम IEnumerable कार्यान्वयन लिखा है जो linq के बिना काम करता है और डेटा पर एकल गणना की गारंटी देता है। यह उन सभी सूचियों या सरणियों की आवश्यकता के बिना भी पूरा करता है जो बड़े डेटा सेट पर मेमोरी विस्फोट का कारण बनते हैं।
यहाँ कुछ बुनियादी परीक्षण दिए गए हैं:
[Fact]
public void ShouldPartition()
{
var ints = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
var data = ints.PartitionByMaxGroupSize(3);
data.Count().Should().Be(4);
data.Skip(0).First().Count().Should().Be(3);
data.Skip(0).First().ToList()[0].Should().Be(0);
data.Skip(0).First().ToList()[1].Should().Be(1);
data.Skip(0).First().ToList()[2].Should().Be(2);
data.Skip(1).First().Count().Should().Be(3);
data.Skip(1).First().ToList()[0].Should().Be(3);
data.Skip(1).First().ToList()[1].Should().Be(4);
data.Skip(1).First().ToList()[2].Should().Be(5);
data.Skip(2).First().Count().Should().Be(3);
data.Skip(2).First().ToList()[0].Should().Be(6);
data.Skip(2).First().ToList()[1].Should().Be(7);
data.Skip(2).First().ToList()[2].Should().Be(8);
data.Skip(3).First().Count().Should().Be(1);
data.Skip(3).First().ToList()[0].Should().Be(9);
}
डेटा को विभाजित करने के लिए एक्सटेंशन विधि।
/// <summary>
/// A set of extension methods for <see cref="IEnumerable{T}"/>.
/// </summary>
public static class EnumerableExtender
{
/// <summary>
/// Splits an enumerable into chucks, by a maximum group size.
/// </summary>
/// <param name="source">The source to split</param>
/// <param name="maxSize">The maximum number of items per group.</param>
/// <typeparam name="T">The type of item to split</typeparam>
/// <returns>A list of lists of the original items.</returns>
public static IEnumerable<IEnumerable<T>> PartitionByMaxGroupSize<T>(this IEnumerable<T> source, int maxSize)
{
return new SplittingEnumerable<T>(source, maxSize);
}
}
यह कार्यान्वयन वर्ग है
using System.Collections;
using System.Collections.Generic;
internal class SplittingEnumerable<T> : IEnumerable<IEnumerable<T>>
{
private readonly IEnumerable<T> backing;
private readonly int maxSize;
private bool hasCurrent;
private T lastItem;
public SplittingEnumerable(IEnumerable<T> backing, int maxSize)
{
this.backing = backing;
this.maxSize = maxSize;
}
public IEnumerator<IEnumerable<T>> GetEnumerator()
{
return new Enumerator(this, this.backing.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class Enumerator : IEnumerator<IEnumerable<T>>
{
private readonly SplittingEnumerable<T> parent;
private readonly IEnumerator<T> backingEnumerator;
private NextEnumerable current;
public Enumerator(SplittingEnumerable<T> parent, IEnumerator<T> backingEnumerator)
{
this.parent = parent;
this.backingEnumerator = backingEnumerator;
this.parent.hasCurrent = this.backingEnumerator.MoveNext();
if (this.parent.hasCurrent)
{
this.parent.lastItem = this.backingEnumerator.Current;
}
}
public bool MoveNext()
{
if (this.current == null)
{
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
else
{
if (!this.current.IsComplete)
{
using (var enumerator = this.current.GetEnumerator())
{
while (enumerator.MoveNext())
{
}
}
}
}
if (!this.parent.hasCurrent)
{
return false;
}
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public IEnumerable<T> Current
{
get { return this.current; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
private class NextEnumerable : IEnumerable<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly IEnumerator<T> backingEnumerator;
private int currentSize;
public NextEnumerable(SplittingEnumerable<T> splitter, IEnumerator<T> backingEnumerator)
{
this.splitter = splitter;
this.backingEnumerator = backingEnumerator;
}
public bool IsComplete { get; private set; }
public IEnumerator<T> GetEnumerator()
{
return new NextEnumerator(this.splitter, this, this.backingEnumerator);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class NextEnumerator : IEnumerator<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly NextEnumerable parent;
private readonly IEnumerator<T> enumerator;
private T currentItem;
public NextEnumerator(SplittingEnumerable<T> splitter, NextEnumerable parent, IEnumerator<T> enumerator)
{
this.splitter = splitter;
this.parent = parent;
this.enumerator = enumerator;
}
public bool MoveNext()
{
this.parent.currentSize += 1;
this.currentItem = this.splitter.lastItem;
var hasCcurent = this.splitter.hasCurrent;
this.parent.IsComplete = this.parent.currentSize > this.splitter.maxSize;
if (this.parent.IsComplete)
{
return false;
}
if (hasCcurent)
{
var result = this.enumerator.MoveNext();
this.splitter.lastItem = this.enumerator.Current;
this.splitter.hasCurrent = result;
}
return hasCcurent;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public T Current
{
get { return this.currentItem; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
}
}
मुझे पता है कि हर कोई इस काम को करने के लिए जटिल प्रणालियों का उपयोग करता है, और मुझे वास्तव में ऐसा क्यों नहीं मिलता है। टेक एंड स्किप आम Func<TSource,Int32,TResult>
फंक्शन वाले ट्रांसफॉर्म फंक्शन के इस्तेमाल से उन सभी ऑपरेशंस की अनुमति देगा । पसंद:
public IEnumerable<IEnumerable<T>> Buffer<T>(IEnumerable<T> source, int size)=>
source.Select((item, index) => source.Skip(size * index).Take(size)).TakeWhile(bucket => bucket.Any());
source
बहुत बार पुनरावृत्त होंगे।
Enumerable.Range(0, 1).SelectMany(_ => Enumerable.Range(0, new Random().Next()))
:।
बस एक और एक लाइन कार्यान्वयन। यह एक खाली सूची के साथ भी काम करता है, इस मामले में आपको एक शून्य आकार का बैच संग्रह मिलता है।
var aList = Enumerable.Range(1, 100).ToList(); //a given list
var size = 9; //the wanted batch size
//number of batches are: (aList.Count() + size - 1) / size;
var batches = Enumerable.Range(0, (aList.Count() + size - 1) / size).Select(i => aList.GetRange( i * size, Math.Min(size, aList.Count() - i * size)));
Assert.True(batches.Count() == 12);
Assert.AreEqual(batches.ToList().ElementAt(0), new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
Assert.AreEqual(batches.ToList().ElementAt(1), new List<int>() { 10, 11, 12, 13, 14, 15, 16, 17, 18 });
Assert.AreEqual(batches.ToList().ElementAt(11), new List<int>() { 100 });
दूसरा तरीका Rx बफर ऑपरेटर का उपयोग कर रहा है
//using System.Linq;
//using System.Reactive.Linq;
//using System.Reactive.Threading.Tasks;
var observableBatches = anAnumerable.ToObservable().Buffer(size);
var batches = aList.ToObservable().Buffer(size).ToList().ToTask().GetAwaiter().GetResult();
GetAwaiter().GetResult()
। यह सिंक्रोनस कोड के लिए एक कोड गंध है जिसे बलपूर्वक async कोड कहते हैं।
static IEnumerable<IEnumerable<T>> TakeBatch<T>(IEnumerable<T> ts,int batchSize)
{
return from @group in ts.Select((x, i) => new { x, i }).ToLookup(xi => xi.i / batchSize)
select @group.Select(xi => xi.x);
}