सूची को एन आकार की छोटी सूची में विभाजित करें


209

मैं एक सूची को छोटी सूचियों की एक श्रृंखला में विभाजित करने का प्रयास कर रहा हूं।

मेरी समस्या: सूचियों को विभाजित करने का मेरा कार्य उन्हें सही आकार की सूचियों में विभाजित नहीं करता है। यह उन्हें आकार 30 की सूची में विभाजित करना चाहिए, लेकिन इसके बजाय उन्हें आकार 114 की सूची में विभाजित करता है?

मैं अपने फ़ंक्शन को 30 या उससे कम आकार की सूची में X संख्या में कैसे विभाजित कर सकता हूं ?

public static List<List<float[]>> splitList(List <float[]> locations, int nSize=30) 
{       
    List<List<float[]>> list = new List<List<float[]>>();

    for (int i=(int)(Math.Ceiling((decimal)(locations.Count/nSize))); i>=0; i--) {
        List <float[]> subLocat = new List <float[]>(locations); 

        if (subLocat.Count >= ((i*nSize)+nSize))
            subLocat.RemoveRange(i*nSize, nSize);
        else subLocat.RemoveRange(i*nSize, subLocat.Count-(i*nSize));

        Debug.Log ("Index: "+i.ToString()+", Size: "+subLocat.Count.ToString());
        list.Add (subLocat);
    }

    return list;
}

यदि मैं फ़ंक्शन का आकार 144 की सूची में उपयोग करता हूं तो आउटपुट है:

सूचकांक: 4, आकार: 120
सूचकांक: 3, आकार: 114
सूचकांक: 2, आकार: 114
सूचकांक: 1, आकार: 114
सूचकांक: 0, आकार: 114


1
यदि कोई LINQ समाधान स्वीकार्य है, तो यह प्रश्न कुछ मदद का हो सकता है

उस पिछले प्रश्न पर विशेष रूप से सैम केसर का उत्तर। और जब तक यह एक स्कूल असाइनमेंट के लिए नहीं है, मैं सिर्फ उसके कोड का उपयोग करूंगा और रोकूंगा।
jcolebrand

जवाबों:


268
public static List<List<float[]>> SplitList(List<float[]> locations, int nSize=30)  
{        
    var list = new List<List<float[]>>(); 

    for (int i = 0; i < locations.Count; i += nSize) 
    { 
        list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i))); 
    } 

    return list; 
} 

सामान्य संस्करण:

public static IEnumerable<List<T>> SplitList<T>(List<T> locations, int nSize=30)  
{        
    for (int i = 0; i < locations.Count; i += nSize) 
    { 
        yield return locations.GetRange(i, Math.Min(nSize, locations.Count - i)); 
    }  
} 

इसलिए अगर मेरे पास एक सूची लंबाई अरब है, और मैं छोटी सूची में विभाजित करना चाहता हूं लंबाई 30, और हर छोटी सूची से मैं केवल (1) लेना चाहता हूं, फिर भी मैं 30 वस्तुओं की सूची बनाता हूं, जिनमें से मैं 29 आइटम फेंक देता हूं। यह होशियार किया जा सकता है!
हराल्ड कोप्पुलसे 15

क्या यह वास्तव में काम करता है? क्या यह पहले विभाजन पर विफल नहीं होगा क्योंकि आपको nSize की सीमा मिल रही है? उदाहरण के लिए यदि nSize 3 है और मेरी सरणी का आकार 5 है तो वापस लौटी पहली इंडेक्स रेंज हैGetRange(3, 3)
मैथ्यू पिगराम

2
@MatthewPigram ने परीक्षण किया और यह काम कर रहा है। Math.Min न्यूनतम मान लेता है इसलिए यदि अंतिम हिस्सा nSize (2 <3) से कम है, तो यह शेष वस्तुओं के साथ एक सूची बनाता है।
फाटे 01

1
@HaraldCoppoolse ओपी ने चयन करने के लिए नहीं कहा, केवल सूचियों को विभाजित करने के लिए
Phate01

@ मैथ्यू पेइग्रम प्रथम पुनरावृत्ति - GetRange (0,3), दूसरा पुनरावृत्ति - GetRange (3,2)
Serj-Tm

381

मैं इस विस्तार विधि का उपयोग करने के लिए स्रोत सूची को उप-सूचियों को निर्दिष्ट चंक आकार द्वारा चंक करने के लिए उपयोग करने का सुझाव दूंगा:

/// <summary>
/// Helper methods for the lists.
/// </summary>
public static class ListExtensions
{
    public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize) 
    {
        return source
            .Select((x, i) => new { Index = i, Value = x })
            .GroupBy(x => x.Index / chunkSize)
            .Select(x => x.Select(v => v.Value).ToList())
            .ToList();
    }
}

उदाहरण के लिए, यदि आप 18 वस्तुओं की सूची को 5 आइटम प्रति चंक करते हैं, तो यह आपको निम्नलिखित मदों के साथ 4 उप-सूचियों की सूची प्रदान करता है: 5-5-5-3।


25
इससे पहले कि आप उत्पादन में इसका उपयोग करें, सुनिश्चित करें कि आप समझते हैं कि स्मृति और प्रदर्शन के लिए रन-टाइम निहितार्थ क्या हैं। सिर्फ इसलिए कि LINQ succinct हो सकता है, इसका मतलब यह नहीं है कि यह एक अच्छा विचार है।
निक

4
निश्चित रूप से, @ कुछ भी करने से पहले मैं सामान्य रूप से सोचने का सुझाव दूंगा। LINQ के साथ चैंकिंग अक्सर हजार बार दोहराया जाने वाला ऑपरेशन नहीं होना चाहिए। आम तौर पर आपको बैच और / या समानांतर द्वारा प्रसंस्करण आइटम बैच के लिए सूचियों को चोक करने की आवश्यकता होती है।
दिमित्री पावलोव

6
मुझे नहीं लगता कि स्मृति और प्रदर्शन यहां एक बड़ा मुद्दा होना चाहिए। मुझे लगभग 3000 प्रत्येक के साथ छोटी सूची में 200,000 से अधिक रिकॉर्ड के साथ एक सूची को विभाजित करने की आवश्यकता हुई, जो मुझे इस धागे में लाया, और मैंने दोनों विधियों का परीक्षण किया और पाया कि चलने का समय लगभग समान है। उसके बाद मैंने उस सूची को 3 रिकॉर्ड के साथ सूचियों में विभाजित करने का परीक्षण किया और अभी भी प्रदर्शन ठीक है। मुझे लगता है कि सर्ज-टीएम का समाधान अधिक सीधा है और हालांकि बेहतर रखरखाव है।
मूक सोजॉर्नर

2
ध्यान दें कि ToList()s को छोड़ना सबसे अच्छा हो सकता है , और आलसी मूल्यांकन को जादू होने दें।
येयर हैलबर्स्ट

3
@DmitryPavlov के दौरान सभी इस समय, मैं कभी-चुने बयान में इस तरह सूचकांक पेश करने में सक्षम होने के बारे में पता था! मैंने सोचा था कि यह एक नई विशेषता है जब तक मैंने देखा कि आपने 2014 में इसे पोस्ट किया था, जिसने मुझे आश्चर्यचकित कर दिया था! इसे साझा करने के लिए धन्यवाद। इसके अलावा, यह विस्तार विधि एक IEnumerable के लिए उपलब्ध है और यह भी एक IEnumerable वापस करने के लिए बेहतर नहीं होगा?
Aydin

37

कैसा रहेगा:

while(locations.Any())
{    
    list.Add(locations.Take(nSize).ToList());
    locations= locations.Skip(nSize).ToList();
}

क्या यह बहुत सारी मेमोरी का उपभोग करने वाला है? हर बार लोकेशन। Sipip.ToList होता है मुझे आश्चर्य होता है कि अधिक मेमोरी आवंटित की जाती है और अनस्किप्ड आइटम को एक नई सूची द्वारा संदर्भित किया जाता है।
ज़ाज़

2
हर लूप पर हाँ नई सूची बनाई जाती है। हाँ यह स्मृति को खा जाता है। लेकिन अगर आपको मेमोरी इश्यू हो रहे हैं तो यह ऑप्टिमाइज़ करने की जगह नहीं है क्योंकि उस सूची के उदाहरण अगले लूप पर एकत्र होने के लिए तैयार हैं। आप स्मृति को लंघन के लिए प्रदर्शन का व्यापार कर सकते हैं, ToListलेकिन मैं इसे अनुकूलित करने की कोशिश नहीं करूंगा - यह बहुत तुच्छ है और संभावना एक अड़चन है। इस कार्यान्वयन से मुख्य लाभ इसकी तुच्छता है जिसे समझना आसान है। यदि आप चाहते हैं कि आप स्वीकृत उत्तर का उपयोग कर सकते हैं तो यह उन सूचियों को नहीं बनाता है, लेकिन थोड़ा अधिक जटिल है।
रफाल

2
.Skip(n)nहर बार इसे बुलाए जाने पर तत्वों पर पुनरावृत्ति होती है , जबकि यह ठीक हो सकता है, प्रदर्शन-महत्वपूर्ण कोड के लिए विचार करना महत्वपूर्ण है। stackoverflow.com/questions/20002975/…
चक्र

@ शकरवा यकीन है, मेरे समाधान का उपयोग प्रदर्शन क्रिटिकल कोड में नहीं किया जाना है, फिर भी मेरे अनुभव में आप पहले वर्किंग कोड लिखते हैं और फिर निर्धारित करते हैं कि प्रदर्शन क्रिटिकल क्या है और यह शायद ही कभी जहाँ वस्तुओं के लिए मेरा linq 50 ऑब्जेक्ट्स पर प्रदर्शन किया। इस मामले का मूल्यांकन किया जाना चाहिए।
रफाल

@ रफाल मैं सहमत हूं, मैंने .Skip()अपनी कंपनी के कोड बेस में कई एस पाए हैं , और जब वे "इष्टतम" नहीं हो सकते हैं तो वे ठीक काम करते हैं। DB ऑपरेशंस जैसी चीजें वैसे भी ज्यादा समय लेती हैं। लेकिन मुझे लगता है कि यह ध्यान रखना एक महत्वपूर्ण बात है कि .Skip()प्रत्येक तत्व <n को अपने रास्ते पर ले जाने के बजाय सीधे nth-element पर कूदें (जैसे कि आप अपेक्षा कर सकते हैं)। यदि किसी तत्व .Skip()को छूने से आपके पुनरावृत्ति के दुष्प्रभाव होते हैं, तो यह मुश्किल से खोजे जाने वाले कीड़े का कारण हो सकता है।
Chakrava

11

सर्ज-टीएम समाधान ठीक है, यह भी सूची के लिए विस्तार विधि के रूप में सामान्य संस्करण है (इसे एक स्थिर वर्ग में डालें):

public static List<List<T>> Split<T>(this List<T> items, int sliceSize = 30)
{
    List<List<T>> list = new List<List<T>>();
    for (int i = 0; i < items.Count; i += sliceSize)
        list.Add(items.GetRange(i, Math.Min(sliceSize, items.Count - i)));
    return list;
} 

10

मुझे स्वीकृत उत्तर (सर्ज-टीएम) सबसे अधिक मजबूत लगता है, लेकिन मैं एक सामान्य संस्करण का सुझाव देना चाहूंगा।

public static List<List<T>> splitList<T>(List<T> locations, int nSize = 30)
{
    var list = new List<List<T>>();

    for (int i = 0; i < locations.Count; i += nSize)
    {
        list.Add(locations.GetRange(i, Math.Min(nSize, locations.Count - i)));
    }

    return list;
}

8

पुस्तकालय MoreLinq विधि कहा जाता है Batch

List<int> ids = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; // 10 elements
int counter = 1;
foreach(var batch in ids.Batch(2))
{
    foreach(var eachId in batch)
    {
        Console.WriteLine("Batch: {0}, Id: {1}", counter, eachId);
    }
    counter++;
}

परिणाम है

Batch: 1, Id: 1
Batch: 1, Id: 2
Batch: 2, Id: 3
Batch: 2, Id: 4
Batch: 3, Id: 5
Batch: 3, Id: 6
Batch: 4, Id: 7
Batch: 4, Id: 8
Batch: 5, Id: 9
Batch: 5, Id: 0

ids 2 तत्वों के साथ 5 विखंडू में विभाजित हैं।


यह स्वीकृत उत्तर होना चाहिए। या इस पृष्ठ पर कम से कम बहुत अधिक है।
ज़ार शारदान

7

मेरे पास एक सामान्य तरीका है जो किसी भी प्रकार को लेगा, जिसमें फ्लोट शामिल है, और यह यूनिट-परीक्षण किया गया है, आशा है कि यह मदद करता है:

    /// <summary>
    /// Breaks the list into groups with each group containing no more than the specified group size
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="values">The values.</param>
    /// <param name="groupSize">Size of the group.</param>
    /// <returns></returns>
    public static List<List<T>> SplitList<T>(IEnumerable<T> values, int groupSize, int? maxCount = null)
    {
        List<List<T>> result = new List<List<T>>();
        // Quick and special scenario
        if (values.Count() <= groupSize)
        {
            result.Add(values.ToList());
        }
        else
        {
            List<T> valueList = values.ToList();
            int startIndex = 0;
            int count = valueList.Count;
            int elementCount = 0;

            while (startIndex < count && (!maxCount.HasValue || (maxCount.HasValue && startIndex < maxCount)))
            {
                elementCount = (startIndex + groupSize > count) ? count - startIndex : groupSize;
                result.Add(valueList.GetRange(startIndex, elementCount));
                startIndex += elementCount;
            }
        }


        return result;
    }

धन्यवाद। आश्चर्य है कि क्या आप अधिकतम पैरामीटर परिभाषा के साथ टिप्पणियों को अपडेट कर सकते हैं? एक सुरक्षा जाल?
एंड्रयू जेन्स

2
असंख्य के कई गणना के साथ सावधान रहें। values.Count()एक पूर्ण गणना और फिर values.ToList()एक और कारण होगा । ऐसा करना सुरक्षित है values = values.ToList()इसलिए यह पहले से ही भौतिक है।
मंध २०'१ 'को १

7

जबकि ऊपर दिए गए बहुत सारे उत्तर काम करते हैं, वे सभी कभी न खत्म होने वाले अनुक्रम (या वास्तव में लंबे अनुक्रम) पर बुरी तरह विफल होते हैं। निम्नलिखित एक पूरी तरह से ऑन-लाइन कार्यान्वयन है जो सर्वोत्तम समय और स्मृति जटिलता की गारंटी देता है। हम केवल एक बार गणना करने योग्य स्रोत को पुनरावृत्त करते हैं और आलसी मूल्यांकन के लिए उपज रिटर्न का उपयोग करते हैं। उपभोक्ता प्रत्येक पुनरावृत्ति पर सूची को दूर कर सकता है जिससे batchSizeतत्वों की सूची w / संख्या के बराबर मेमोरी फुटप्रिंट बन सके ।

public static IEnumerable<List<T>> BatchBy<T>(this IEnumerable<T> enumerable, int batchSize)
{
    using (var enumerator = enumerable.GetEnumerator())
    {
        List<T> list = null;
        while (enumerator.MoveNext())
        {
            if (list == null)
            {
                list = new List<T> {enumerator.Current};
            }
            else if (list.Count < batchSize)
            {
                list.Add(enumerator.Current);
            }
            else
            {
                yield return list;
                list = new List<T> {enumerator.Current};
            }
        }

        if (list?.Count > 0)
        {
            yield return list;
        }
    }
}

संपादित करें: अभी-अभी ओपी को साकार List<T>करते हुए छोटे में तोड़ने के बारे में पूछता है List<T>, इसलिए अनंत एनुमरबेल के बारे में मेरी टिप्पणी ओपी पर लागू नहीं होती है, लेकिन दूसरों की मदद कर सकती है जो यहां समाप्त होते हैं। ये टिप्पणियां अन्य पोस्ट किए गए समाधानों के जवाब में थीं जो IEnumerable<T>उनके फ़ंक्शन के लिए एक इनपुट के रूप में उपयोग करते हैं , फिर भी स्रोत को कई बार गणना करते हैं।


मुझे लगता है कि IEnumerable<IEnumerable<T>>संस्करण बेहतर है क्योंकि इसमें इतना Listनिर्माण शामिल नहीं है ।
नेटमैज

@NetMage - इसके साथ एक मुद्दा IEnumerable<IEnumerable<T>>यह है कि कार्यान्वयन उपभोक्ता पर निर्भर करने की संभावना है कि वह प्रत्येक आंतरिक गणना योग्य उपज पर पूरी तरह से भरोसा कर सके। मुझे यकीन है कि एक समाधान उस मुद्दे से बचने के लिए एक तरह से किया जा सकता है, लेकिन मुझे लगता है कि परिणामस्वरूप कोड बहुत जल्दी जटिल हो सकता है। इसके अलावा, चूंकि यह आलसी है, इसलिए हम केवल एक ही समय में एक ही सूची तैयार कर रहे हैं और मेमोरी आवंटन प्रति सूची में एक बार होता है क्योंकि हम आकार को सामने जानते हैं।
एमएच

आप सही हैं - मेरा कार्यान्वयन एक नए प्रकार के एन्यूमरेटर (एक स्थिति एन्यूमरेटर) का उपयोग करता है जो एक मानक एन्यूमरेटर को लपेटकर आपकी वर्तमान स्थिति को ट्रैक करता है और आपको एक नई स्थिति में ले जाता है।
NetMage

6

अंत में mhand की बहुत उपयोगी टिप्पणी के बाद जोड़

मूल उत्तर

यद्यपि अधिकांश समाधान काम कर सकते हैं, मुझे लगता है कि वे बहुत कुशलता से नहीं हैं। मान लीजिए अगर आप केवल पहले कुछ चंक्स के पहले आइटम चाहते हैं। तब आप अपने अनुक्रम में सभी (zillion) वस्तुओं पर पुनरावृति नहीं करना चाहेंगे।

निम्नलिखित दो बार अत्यधिक गणना करेगा: एक बार टेक के लिए और एक बार स्किप के लिए। यह आपके द्वारा उपयोग किए जाने वाले किसी भी अधिक तत्वों की गणना नहीं करेगा:

public static IEnumerable<IEnumerable<TSource>> ChunkBy<TSource>
    (this IEnumerable<TSource> source, int chunkSize)
{
    while (source.Any())                     // while there are elements left
    {   // still something to chunk:
        yield return source.Take(chunkSize); // return a chunk of chunkSize
        source = source.Skip(chunkSize);     // skip the returned chunk
    }
}

इस क्रम को कितनी बार दोहराना होगा?

मान लीजिए कि आप अपने स्रोत को विखंडू में विभाजित करते हैं chunkSize। आप केवल पहले एन विखंडू की गणना करते हैं। हर एन्यूमरेटेड चंक से आप केवल पहले एम एलिमेंट्स को एन्यूमरेट करेंगे।

While(source.Any())
{
     ...
}

किसी को भी एन्यूमरेटर मिलेगा, 1 मूवनेट () करें और एन्यूमरेटर को डिस्पोज करने के बाद लौटाया हुआ मान लौटाएं। यह एन बार किया जाएगा

yield return source.Take(chunkSize);

संदर्भ स्रोत के अनुसार यह कुछ ऐसा करेगा:

public static IEnumerable<TSource> Take<TSource>(this IEnumerable<TSource> source, int count)
{
    return TakeIterator<TSource>(source, count);
}

static IEnumerable<TSource> TakeIterator<TSource>(IEnumerable<TSource> source, int count)
{
    foreach (TSource element in source)
    {
        yield return element;
        if (--count == 0) break;
    }
}

यह तब तक बहुत कुछ नहीं करता है जब तक कि आप भ्रूण वाले चंक के ऊपर से गुजरना शुरू नहीं करते हैं। यदि आप कई चंक्स लाते हैं, लेकिन यह तय करें कि पहले चंक पर अधिक ध्यान न दें, तो फॉरेस्ट निष्पादित नहीं होता है, क्योंकि आपका डिबगर आपको दिखाएगा।

यदि आप पहले चंक के पहले M तत्वों को लेने का निर्णय लेते हैं तो पैदावार रिटर्न बिल्कुल M बार निष्पादित किया जाता है। इसका मतलब है की:

  • गणक प्राप्त करें
  • MoveNext () और वर्तमान M समय को कॉल करें।
  • एन्यूमरेटर को डिस्पोज करें

पहले चंक की उपज वापस आने के बाद, हम इस पहले चंक को छोड़ देते हैं:

source = source.Skip(chunkSize);

एक बार फिर: हम खोजने के लिए संदर्भ स्रोत पर एक नज़र डालेंगेskipiterator

static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
    using (IEnumerator<TSource> e = source.GetEnumerator()) 
    {
        while (count > 0 && e.MoveNext()) count--;
        if (count <= 0) 
        {
            while (e.MoveNext()) yield return e.Current;
        }
    }
}

जैसा कि आप देखते हैं, चंक में हर तत्व के लिए एक बार SkipIteratorकॉल करता है MoveNext()यह फोन नहीं करता है Current

तो प्रति Chunk हम देखते हैं कि निम्नलिखित किया जाता है:

  • कोई (): GetEnumerator; 1 MoveNext (); डिस्पोजल एन्यूमरेटर;
  • लेना():

    • कुछ भी नहीं है अगर chunk की सामग्री enumerated नहीं है।
    • यदि सामग्री enumerated है: GetEnumerator (), एक MoveNext और एक वर्तमान प्रति enumerated आइटम, Disumerumer;

    • छोड़ें (): हर उस टुकड़ी के लिए जो एन्यूमरेटेड है (कंटेंट नहीं): GetEnumerator (), MoveNext () chunkSize times, no current! गणना करनेवाला

यदि आप देखते हैं कि एन्यूमरेटर के साथ क्या होता है, तो आप देखेंगे कि MoveNext () के लिए बहुत सारे कॉल हैं, और केवल CurrentTSource आइटम के लिए कॉल करते हैं जिन्हें आप वास्तव में एक्सेस करने का निर्णय लेते हैं।

यदि आप आकार का एन चंक्स लेते हैं, तो MoveNext () को कॉल करें

  • किसी भी समय ()
  • टेक के लिए अभी तक कोई समय नहीं है, जब तक आप चंक्स की गणना नहीं करते हैं
  • एन बार स्किप करने के लिए ()

यदि आप हर आने वाले चंक के केवल पहले M तत्वों की गणना करने का निर्णय लेते हैं, तो आपको प्रति Enumerated Chunk को MoveNext M बार कॉल करना होगा।

समूचा

MoveNext calls: N + N*M + N*chunkSize
Current calls: N*M; (only the items you really access)

इसलिए यदि आप सभी विखंडू के सभी तत्वों की गणना करने का निर्णय लेते हैं:

MoveNext: numberOfChunks + all elements + all elements = about twice the sequence
Current: every item is accessed exactly once

MoveNext बहुत काम है या नहीं, स्रोत अनुक्रम के प्रकार पर निर्भर करता है। सूचियों और सरणियों के लिए यह एक साधारण सूचकांक वृद्धि है, शायद एक सीमा से बाहर की जाँच।

लेकिन अगर आपका IEnumerable डेटाबेस क्वेरी का परिणाम है, तो सुनिश्चित करें कि डेटा वास्तव में आपके कंप्यूटर पर भौतिक है, अन्यथा डेटा को कई बार लाया जाएगा। DbContext और Dapper एक्सेस होने से पहले डेटा को स्थानीय प्रक्रिया में ठीक से स्थानांतरित कर देगा। यदि आप एक ही क्रम को कई बार दोहराते हैं तो यह कई बार नहीं आता है। Dapper एक ऑब्जेक्ट देता है जो एक सूची है, DbContext याद रखता है कि डेटा पहले से ही प्राप्त है।

यह आपके रिपॉजिटरी पर निर्भर करता है कि क्या चांस में आइटम विभाजित करना शुरू करने से पहले आपको AsEnumerable () या ToLists () को कॉल करना बुद्धिमान है


यह प्रति बैच दो बार गणना नहीं करेगा ? तो हम वास्तव में स्रोत 2*chunkSizeसमय की गणना कर रहे हैं ? यह गणना करने योग्य (शायद डीबी समर्थित, या अन्य गैर-ज्ञापन स्रोत) के स्रोत के आधार पर घातक है। इनपुट के रूप में इस कल्पना करने योग्य की कल्पना करें Enumerable.Range(0, 10000).Select(i => DateTime.UtcNow)- जब आप याद नहीं करते हैं तो आपको हर बार अलग-अलग बार मिलेंगे, जब तक यह याद नहीं होगा
mhand

पर विचार करें: Enumerable.Range(0, 10).Select(i => DateTime.UtcNow)। आह्वान करके Anyआप प्रत्येक बार वर्तमान समय को फिर से प्रकाशित करेंगे। के लिए इतना बुरा नहीं है DateTime.UtcNow, लेकिन एक डेटाबेस कनेक्शन / एसक्यूएल कर्सर या इसी तरह से समर्थित एक एन्यूमरेबल पर विचार करें। मैंने ऐसे मामले देखे हैं जहां हजारों DB कॉल जारी किए गए क्योंकि डेवलपर ने ' एक
एन्युमरबल के

4
public static IEnumerable<IEnumerable<T>> SplitIntoSets<T>
    (this IEnumerable<T> source, int itemsPerSet) 
{
    var sourceList = source as List<T> ?? source.ToList();
    for (var index = 0; index < sourceList.Count; index += itemsPerSet)
    {
        yield return sourceList.Skip(index).Take(itemsPerSet);
    }
}

3
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));
}

2

इसके बारे में क्या खयाल है? विचार केवल एक लूप का उपयोग करना था। और, कौन जानता है, हो सकता है कि आप केवल अपने कोड को पूरी तरह से लागू कर रहे हों और आप सूची में नहीं डालना चाहते।

private IEnumerable<IList<T>> SplitList<T>(IList<T> list, int totalChunks)
{
    IList<T> auxList = new List<T>();
    int totalItems = list.Count();

    if (totalChunks <= 0)
    {
        yield return auxList;
    }
    else 
    {
        for (int i = 0; i < totalItems; i++)
        {               
            auxList.Add(list[i]);           

            if ((i + 1) % totalChunks == 0)
            {
                yield return auxList;
                auxList = new List<T>();                
            }

            else if (i == totalItems - 1)
            {
                yield return auxList;
            }
        }
    }   
}

1

एक और

public static IList<IList<T>> SplitList<T>(this IList<T> list, int chunkSize)
{
    var chunks = new List<IList<T>>();
    List<T> chunk = null;
    for (var i = 0; i < list.Count; i++)
    {
        if (i % chunkSize == 0)
        {
            chunk = new List<T>(chunkSize);
            chunks.Add(chunk);
        }
        chunk.Add(list[i]);
    }
    return chunks;
}

1
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
    {           
        var result = new List<List<T>>();
        for (int i = 0; i < source.Count; i += chunkSize)
        {
            var rows = new List<T>();
            for (int j = i; j < i + chunkSize; j++)
            {
                if (j >= source.Count) break;
                rows.Add(source[j]);
            }
            result.Add(rows);
        }
        return result;
    }

0
List<int> list =new List<int>(){1,2,3,4,5,6,7,8,9,10,12};
Dictionary<int,List<int>> dic = new Dictionary <int,List<int>> ();
int batchcount = list.Count/2; //To List into two 2 parts if you want three give three
List<int> lst = new List<int>();
for (int i=0;i<list.Count; i++)
{
lstdocs.Add(list[i]);
if (i % batchCount == 0 && i!=0)
{
Dic.Add(threadId, lstdocs);
lst = new List<int>();**strong text**
threadId++;
}
}
Dic.Add(threadId, lstdocs);

2
केवल एक कोड स्निपेट प्रदान करने के बजाय अपने उत्तर की व्याख्या करना बेहतर होगा
केविन

0

मुझे इसकी समान आवश्यकता का सामना करना पड़ा था, और मैंने Linq's Skip () और Take () विधियों के संयोजन का उपयोग किया । मैं इसे अब तक पुनरावृत्तियों की संख्या से गुणा करता हूं, और इससे मुझे आइटम को छोड़ने की संख्या मिलती है, फिर मैं अगला समूह लेता हूं।

        var categories = Properties.Settings.Default.MovementStatsCategories;
        var items = summariesWithinYear
            .Select(s =>  s.sku).Distinct().ToList();

        //need to run by chunks of 10,000
        var count = items.Count;
        var counter = 0;
        var numToTake = 10000;

        while (count > 0)
        {
            var itemsChunk = items.Skip(numToTake * counter).Take(numToTake).ToList();
            counter += 1;

            MovementHistoryUtilities.RecordMovementHistoryStatsBulk(itemsChunk, categories, nLogger);

            count -= numToTake;
        }

0

दिमित्री पावलोव के जवाब के आधार पर मैं हटा दूंगा.ToList() । और अनाम वर्ग से भी बचें। इसके बजाय मुझे एक संरचना का उपयोग करना पसंद है जिसमें ढेर मेमोरी आवंटन की आवश्यकता नहीं है। ( ValueTupleनौकरी भी करेगा।)

public static IEnumerable<IEnumerable<TSource>> ChunkBy<TSource>(this IEnumerable<TSource> source, int chunkSize)
{
    if (source is null)
    {
        throw new ArgumentNullException(nameof(source));
    }
    if (chunkSize <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(chunkSize), chunkSize, "The argument must be greater than zero.");
    }

    return source
        .Select((x, i) => new ChunkedValue<TSource>(x, i / chunkSize))
        .GroupBy(cv => cv.ChunkIndex)
        .Select(g => g.Select(cv => cv.Value));
} 

[StructLayout(LayoutKind.Auto)]
[DebuggerDisplay("{" + nameof(ChunkedValue<T>.ChunkIndex) + "}: {" + nameof(ChunkedValue<T>.Value) + "}")]
private struct ChunkedValue<T>
{
    public ChunkedValue(T value, int chunkIndex)
    {
        this.ChunkIndex = chunkIndex;
        this.Value = value;
    }

    public int ChunkIndex { get; }

    public T Value { get; }
}

यह निम्नलिखित की तरह इस्तेमाल किया जा सकता है जो केवल एक बार संग्रह पर पुनरावृत्ति करता है और किसी भी महत्वपूर्ण मेमोरी को आवंटित नहीं करता है।

int chunkSize = 30;
foreach (var chunk in collection.ChunkBy(chunkSize))
{
    foreach (var item in chunk)
    {
        // your code for item here.
    }
}

अगर वास्तव में एक ठोस सूची की जरूरत है तो मैं इसे इस तरह से करूंगा:

int chunkSize = 30;
var chunkList = new List<List<T>>();
foreach (var chunk in collection.ChunkBy(chunkSize))
{
    // create a list with the correct capacity to be able to contain one chunk
    // to avoid the resizing (additional memory allocation and memory copy) within the List<T>.
    var list = new List<T>(chunkSize);
    list.AddRange(chunk);
    chunkList.Add(list);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.